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<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 |
|
||||
| ```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
|
||||
|---|---|
|
||||
| ```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
|
||||
| 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} &&
|
||||
|
||||
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"
|
||||
genericModel::genericModel(const std::string& storepath, const std::string &id, int index) : storepath(storepath), id(id), index(index) {
|
||||
fs::current_path("/opfs");
|
||||
fs::create_directories(storepath);
|
||||
fs::current_path(storepath);
|
||||
fs::current_path("/opfs", tank);
|
||||
if(tank.value() != 0) {
|
||||
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() {
|
||||
if(!checkModelFiles()) return false;
|
||||
static std::error_code c{};
|
||||
if(!fs::exists("id", c)) return false;
|
||||
if(!fs::exists("id", tank)) return false;
|
||||
std::ifstream file {"id", std::ifstream::binary};
|
||||
if(!file.is_open()) return false;
|
||||
long long size {file.seekg(0, std::ios::end).tellg()};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "global.h"
|
||||
|
||||
static pthread_t selfTID{pthread_self()};
|
||||
void throwJS(const char* msg, bool err) {
|
||||
EM_ASM({
|
||||
if($1) {
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
#include <emscripten/console.h>
|
||||
#include <emscripten/em_asm.h>
|
||||
#include <emscripten/proxying.h>
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
static pthread_t selfTID{pthread_self()};
|
||||
static std::error_code tank{};
|
||||
void throwJS(const char* msg, bool err = false);
|
||||
void fireEv(const char *type, const char *content, int index);
|
||||
int main();
|
||||
|
||||
27
src/model.cc
27
src/model.cc
@@ -28,18 +28,17 @@ void model::load(bool newThrd) {
|
||||
t.detach();
|
||||
}
|
||||
bool model::checkModelFiles() {
|
||||
static std::error_code c{};
|
||||
return fs::exists("am/final.mdl", c) &&
|
||||
fs::exists("conf/mfcc.conf", c) &&
|
||||
fs::exists("conf/model.conf", c) &&
|
||||
fs::exists("graph/phones/word_boundary.int", c) &&
|
||||
fs::exists("graph/Gr.fst", c) &&
|
||||
fs::exists("graph/HCLr.fst", c) &&
|
||||
fs::exists("graph/disambig_tid.int", c) &&
|
||||
fs::exists("ivector/final.dubm", c) &&
|
||||
fs::exists("ivector/final.ie", c) &&
|
||||
fs::exists("ivector/final.mat", c) &&
|
||||
fs::exists("ivector/global_cmvn.stats", c) &&
|
||||
fs::exists("ivector/online_cmvn.conf", c) &&
|
||||
fs::exists("ivector/splice.conf", c);
|
||||
return fs::exists("am/final.mdl", tank) &&
|
||||
fs::exists("conf/mfcc.conf", tank) &&
|
||||
fs::exists("conf/model.conf", tank) &&
|
||||
fs::exists("graph/phones/word_boundary.int", tank) &&
|
||||
fs::exists("graph/Gr.fst", tank) &&
|
||||
fs::exists("graph/HCLr.fst", tank) &&
|
||||
fs::exists("graph/disambig_tid.int", tank) &&
|
||||
fs::exists("ivector/final.dubm", tank) &&
|
||||
fs::exists("ivector/final.ie", tank) &&
|
||||
fs::exists("ivector/final.mat", tank) &&
|
||||
fs::exists("ivector/global_cmvn.stats", tank) &&
|
||||
fs::exists("ivector/online_cmvn.conf", tank) &&
|
||||
fs::exists("ivector/splice.conf", tank);
|
||||
}
|
||||
216
src/pre.js
216
src/pre.js
@@ -1,4 +1,25 @@
|
||||
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 {
|
||||
constructor() {
|
||||
super()
|
||||
@@ -69,11 +90,13 @@ class SpkModel extends EventTarget {
|
||||
this.obj.delete()
|
||||
}
|
||||
}
|
||||
Module.deleteAll = () => {
|
||||
objs.forEach(obj => obj.delete())
|
||||
}
|
||||
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) => {
|
||||
mdl.addEventListener("_continue", (ev) => {
|
||||
if(ev.detail === ".") {
|
||||
@@ -99,7 +122,12 @@ Module.makeModel = 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) => {
|
||||
mdl.addEventListener("_continue", (ev) => {
|
||||
if(ev.detail === ".") {
|
||||
@@ -139,3 +167,181 @@ Module.makeRecognizer = (model, sampleRate) => {
|
||||
rec._init(model.obj, sampleRate)
|
||||
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
|
||||
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();
|
||||
}
|
||||
bool spkModel::checkModelFiles() {
|
||||
return fs::exists("mfcc.conf") &&
|
||||
fs::exists("final.ext.raw") &&
|
||||
fs::exists("mean.vec") &&
|
||||
fs::exists("transform.mat");
|
||||
return fs::exists("mfcc.conf", tank) &&
|
||||
fs::exists("final.ext.raw", tank) &&
|
||||
fs::exists("mean.vec", tank) &&
|
||||
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 &&
|
||||
|
||||
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