Add findWord to model object, update docs and change test directory

This commit is contained in:
msqr1
2024-04-28 21:46:36 -07:00
parent b7df449ae0
commit 0342e66e74
15 changed files with 69 additions and 171 deletions

4
.gitignore vendored
View File

@@ -4,4 +4,6 @@ libarchive
.vscode
clapack-wasm
openfst
emsdk
emsdk
index.html
test.js

49
API.md
View File

@@ -1,8 +1,8 @@
# API reference
## JS' ```window``` object
## JS ```window``` object
| Function/Object | Description |
|---|---|
|```Promise<Module> loadVosklet()``` | Load Vosklet module interface |
| ```Promise<Module> loadVosklet()``` | Load Vosklet module interface |
## Shared interface
| Function/Object | Description |
@@ -12,36 +12,41 @@
## ```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 .tgz format. 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, reuse them as much as possible! |
| ```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```, it will use **model**'s thread if it's the first user of **model**, else it will use a new thread. |
| ```setLogLevel(lvl: int)``` | Set Vosk's log level (default: ```0```: Info) <br>```-2```: Error<br>```-1```: Warning<br>```1```: Verbose<br>```2```: More verbose<br>```3```: Debug |
| ```Promise<AudioWorkletNode> createTransferer(ctx: AudioContext, bufferSize: int)``` | Create a node that transfer its inputs back to the main thread with custom buffer size (must be multiple of 128). Its port's ```onmessage``` handler can be set to get audio data. Has 1 input with 1 channel and 0 output. The the higher the size, the lesser the audio breaks up, but the higher the latency. Recomended value is around ```128 * 150```. |
| ```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 .tgz format. 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```, it will reuse the thread from ```model``` if it's the first user of ```model```, else it will use a new thread. |
| ```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 |
| ```Promise<AudioWorkletNode> createTransferer(ctx: AudioContext, bufferSize: int)``` | Create a node that transfer its inputs back to the main thread with custom buffer size (must be multiple of 128). Its port's ```onmessage``` handler can be set to get audio data. Has 1 input with 1 channel and no output. The the higher the size, the lesser the audio breaks up, but the higher the latency. Recomended value is around ```128 * 150```. |
| ```cleanUp()``` | A convenience function that call ```delete()``` on all objects and revoke all URLs. **Put this at the end of your code!** |
| ```epMode``` | Endpointer modes (enum) | See Vosk's description |
| ```epMode``` | Enum for endpointer modes | See Vosk's description |
## ```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. |
## ```Recognizer``` object
| Function/Object | Description |
|---|---|
| ```acceptWaveform(audioData: Float32Array)``` | Recognize audio data in a ```Float32Array``` with elements from -1.0 to 1.0. |
| ```setPartialWords(partialWords: bool)``` | See Vosk's description (default: false) |
| ```setWords(words: bool)``` | See Vosk's description (default: false) |
| ```setNLSML(nlsml: bool)``` | See Vosk's description (default: false) |
| ```setMaxAlternatives(alts: int)``` | See Vosk's description (default: false) |
| ```setGrm(grm: string)``` | See Vosk's description (default: none) |
| ```setSpkModel(mdl: SpkModel)``` | See Vosk's description (default: none) |
| ```setEndpointerMode(mode: epMode)``` | See Vosk's description (default: ANSWER_DEFAULT) |
| ```setEndpointerDelays(tStartMax: float, tEnd: float, tMax: float)``` | See Vosk's description |
| ```acceptWaveform(audioData: Float32Array)``` | Accept voice data in a ```Float32Array``` with elements from ```-1.0``` to ```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```) |
| ```setMaxAlternatives(alts: int)``` | Configures recognizer to output n-best results (default: ```false```) |
| ```setGrm(grm: string)``` | Reconfigures recognizer to use grammar |
| ```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 |
| 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 |
| ```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 |
# User agent notes
## SharedArrayBuffer
Vosklet require SharedArrayBuffer to share threads' data, so these response headers must be set:
- ***Cross-Origin-Embedder-Policy*** ---> ***require-corp***
- ***Cross-Origin-Opener-Policy*** ---> ***same-origin***
SharedArrayBuffer is necessary to share data between threads, so these response headers must be set:
- ```Cross-Origin-Embedder-Policy``` ⟶ ```require-corp```
- ```Cross-Origin-Opener-Policy``` ⟶ ```same-origin```
If you can't set them, you may use a hacky workaround at *src/addCOI.js*.
@@ -58,6 +63,6 @@ cd Vosklet/src &&
| Option | Description | Default value |
|---|---|---|
| MAX_MEMORY | Set max memory, valid suffixes: kb, mb, gb, tb or none (bytes) | ```300mb``` as [recommended](https://alphacephei.com/vosk/models) |
| MAX_THREADS | Set the max number of threads (>=1), this should be equal to the number of model or speaker model that is used in the program | ```1``` (1 recognizer, 1 model, 0 speaker model) |
| MAX_THREADS | Set the max number of threads (>=1), this should be equal to the number of model or speaker model that is used in the program | ```1``` (1 recognizer, 1 model, no speaker model) |
| JOBS | Set the number of jobs (threads) when building | ```$(nproc)``` |
| EMSDK | Set EMSDK's path (will install EMSDK in root folder if unset) | ```../emsdk``` |

View File

@@ -3,7 +3,7 @@
- Designed with basic/nothrow exception safety
- See the *examples* folder for examples on using the API.
- See *API.md* for the API reference
- See the *devel* folder for the build script for development
- See *test* for a developer build script for just the JS
# Compared to vosk-browser
- Support multiple models
@@ -22,7 +22,7 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.0.0/Vosklet.js" async defer></script>
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.0.2/Vosklet.js" async defer></script>
<script>
async function start() {
// Make sure sample rate matches that in the training data

File diff suppressed because one or more lines are too long

View File

@@ -1,35 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script src="../Vosklet.js" async defer></script>
<script>
async function start() {
let ctx = new AudioContext({sampleRate : 16000})
let micNode = ctx.createMediaStreamSource(await navigator.mediaDevices.getUserMedia({
video: false,
audio: {
echoCancellation: true,
noiseSuppression: true,
channelCount: 1,
sampleRate: 16000
},
}))
let module = await loadVosklet()
let model = await module.createModel("en-model.tgz","model","ID")
let recognizer = await module.createRecognizer(model, 16000)
recognizer.addEventListener("result", ev => {
console.log("Result: ", ev.detail)
})
recognizer.addEventListener("partialResult", ev => {
console.log("Partial result: ", ev.detail)
})
let transferer = await module.createTransferer(ctx, 25600)
transferer.port.onmessage = ev => {
recognizer.acceptWaveform(ev.data)
}
micNode.connect(transferer)
}
</script>
<button onclick="start()">Start</button>
</head>
</html>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.0.0/Vosklet.js" async defer></script>
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.0.2/Vosklet.js" async defer></script>
<script>
async function start() {
// Make sure sample rate matches that in the training data

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.0.0/Vosklet.js" async defer></script>
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.0.2/Vosklet.js" async defer></script>
<script>
async function start() {
// Make sure sample rate matches that in the training data

View File

@@ -1,65 +0,0 @@
# Overview
- A speech recognizer built on Vosk that can be run on the browser, inspired by [vosk-browser](https://github.com/ccoreilly/vosk-browser), but built from scratch and no code taken!
- Designed with basic/nothrow exception safety
- See the *examples* folder for examples on using the API.
- See *API.md* for the API reference
- See the *devel* folder for the build script for development
# Compared to vosk-browser
- Support multiple models
- Has models' storage path management
- Has models' ID management (for model updates)
- Has smaller JS size (>3.1MB vs 1.4MB gzipped)
- Has all related files (pthread worker, audio worklet processor,...) merged
- Has faster processing time
- Has shorter from-scratch build time
- Has more Vosk functions exposed
# Basic usage (microphone recognition)
- Result are logged to the console.
- Copied from *examples/fromMic.html*
```html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.0.0/Vosklet.js" async defer></script>
<script>
async function start() {
// Make sure sample rate matches that in the training data
let ctx = new AudioContext({sampleRate : 16000})
// Setup mic with correct sample rate
let micNode = ctx.createMediaStreamSource(await navigator.mediaDevices.getUserMedia({
video: false,
audio: {
echoCancellation: true,
noiseSuppression: true,
channelCount: 1,
sampleRate: 16000
},
}))
// Load Vosklet module, model and recognizer
let module = await loadVosklet()
let model = await module.createModel("en-model.tgz","model","ID")
let recognizer = await module.createRecognizer(model, 16000)
// Listen for result and partial result
recognizer.addEventListener("result", ev => {
console.log("Result: ", ev.detail)
})
recognizer.addEventListener("partialResult", ev => {
console.log("Partial result: ", ev.detail)
})
// Create a transferer node to get audio data on the main thread
let transferer = await module.createTransferer(ctx, 128 * 150)
// Recognize data on arrival
transferer.port.onmessage = ev => {
recognizer.acceptWaveform(ev.data)
}
// Connect to microphone
micNode.connect(transferer)
}
</script>
<!-- Start and create audio context only as a result of user's action -->
<button onclick="start()">Start</button>
</head>
</html>
```

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
"name": "vosklet",
"version": "1.0.1",
"description": "A speech recognizer that can run on the browser, inspired by vosk-browser",
"main": "index.js",
"main": "Vosklet.js",
"directories": {
"example": "examples"
},

View File

@@ -15,7 +15,8 @@ EMSCRIPTEN_BINDINGS() {
class_<genericModel>("genericModel")
.constructor<int, bool, std::string, std::string>(allow_raw_pointers())
.function("extractAndLoad", &genericModel::extractAndLoad, allow_raw_pointers());
.function("extractAndLoad", &genericModel::extractAndLoad, allow_raw_pointers())
.function("findWord", &genericModel::findWord, allow_raw_pointers());
class_<recognizer>("recognizer")
.constructor<int, float, genericModel*>(allow_raw_pointers())

View File

@@ -54,6 +54,9 @@ void genericModel::extractAndLoad(int tarStart, int tarSize) {
}};
t.detach();
}
int genericModel::findWord(std::string word) {
return vosk_model_find_word(std::get<VoskModel*>(mdl), word.c_str());
}
genericModel::~genericModel() {
archive_entry_free(entry);
if(normalMdl) vosk_model_free(std::get<VoskModel*>(mdl));

View File

@@ -23,6 +23,7 @@ struct genericModel {
std::function<void()> func;
archive_entry* entry;
void extractAndLoad(int tarStart, int tarSize);
int findWord(std::string word);
genericModel(int index, bool normalMdl, std::string storepath, std::string id);
~genericModel();
};

View File

@@ -19,28 +19,30 @@ let processorURL = URL.createObjectURL(new Blob(['(', (() => {
}
})
}).toString(), ')()'], { type : "text/javascript" }))
Module.cleanUp = () => {
objs.forEach(obj => obj.obj.delete())
URL.revokeObjectURL(processorURL)
}
Module.createTransferer = async (ctx, bufferSize) => {
await ctx.audioWorklet.addModule(processorURL)
return new AudioWorkletNode(ctx, "VoskletTransferer", {
channelCountMode : "explicit",
numberOfInputs : 1,
numberOfOutputs : 1,
numberOfOutputs : 0,
channelCount : 1,
processorOptions : { maxCount: bufferSize / 128 }
})
}
async function getFileHandle(path, create = false) {
let components = path.split("/")
let prevDir = await navigator.storage.getDirectory()
for(let component of components.slice(0, -1)) {
prevDir = await prevDir.getDirectoryHandle(component, { create : create })
}
for(let component of components.slice(0, -1)) prevDir = await prevDir.getDirectoryHandle(component, { create : create })
return prevDir.getFileHandle(components[components.length - 1], { create : create })
}
class genericModel extends EventTarget {
constructor() {
super()
@@ -50,7 +52,10 @@ class genericModel extends EventTarget {
let mdl = new genericModel()
let result = new Promise((resolve, reject) => {
mdl.addEventListener("0", ev => {
if(ev.detail === "0") return resolve(mdl)
if(ev.detail == "0") {
if(normalMdl) mdl.findWord = (word) => mdl.obj.findWord(word)
return resolve(mdl)
}
mdl.delete()
reject(ev.detail)
}, { once : true })
@@ -60,15 +65,13 @@ class genericModel extends EventTarget {
try {
let dataFile = await (await getFileHandle(storepath + "/model.tgz")).getFile()
let idFile = await (await getFileHandle(storepath + "/id")).getFile()
if(await idFile.text() !== id) throw ""
if(await idFile.text() != id) throw ""
tar = dataFile.stream()
}
catch {
try {
let res = await fetch(url)
if(!res.ok) {
throw "Unable to download model"
}
if(!res.ok) throw "Unable to download model"
let teedBody = res.body.tee()
let newDataFile = await (await getFileHandle(storepath + "/model.tgz", true)).createWritable()
await newDataFile.write(await new Response(teedBody[0]).arrayBuffer())
@@ -79,7 +82,7 @@ class genericModel extends EventTarget {
tar = teedBody[1]
}
catch(e) {
mdl.obj.delete()
mdl.delete()
throw e
}
}
@@ -93,12 +96,15 @@ class genericModel extends EventTarget {
this.obj.delete()
}
}
Module.createModel = async (url, storepath, id) => {
return genericModel.create(url, storepath, id, true)
}
Module.createSpkModel = async (url, storepath, id) => {
return genericModel.create(url, storepath, id, false)
}
class recognizer extends EventTarget {
constructor() {
super()
@@ -113,7 +119,7 @@ class recognizer extends EventTarget {
let rec = new recognizer()
let result = new Promise((resolve, reject) => {
rec.addEventListener("0", ev => {
if(ev.detail === "0") return resolve(rec)
if(ev.detail == "0") return resolve(rec)
rec.delete()
reject(ev.detail)
}, { once : true })
@@ -136,12 +142,15 @@ class recognizer extends EventTarget {
this.obj.pushData(start, audioData.length)
}
}
Module.createRecognizer = (model, sampleRate) => {
return recognizer.create(model.obj, sampleRate, 1)
}
Module.createRecognizerWithSpkModel = (model, sampleRate, spkModel) => {
return recognizer.create(model.obj, sampleRate, 2, null, spkModel)
}
}
Module.createRecognizerWithGrm = (model, sampleRate, grammar) => {
return recognizer.create(model.obj, sampleRate, 3, grammar, null)
}
}

View File

@@ -30,21 +30,20 @@ fi
. $(realpath $EMSDK)/emsdk_env.sh &&
export PATH=:$PATH:$(realpath $EMSDK)/upstream/bin
cd ..
KALDI=$(realpath kaldi)
VOSK=$(realpath vosk)
OPENFST=$(realpath openfst)
LIBARCHIVE=$(realpath libarchive)
CLAPACK_WASM=$(realpath clapack-wasm)
cd $(realpath src)
MODE=1 && # 0: Ultra debug info, 1: Extremely optimized release, else custom
cd src &&
MODE=0 && # 0: Ultra debug info, 1: Optimized release, else custom
echo "Mode = $MODE" &&
if [ $MODE = 0 ]; then
em++ -O0 link.cc genericModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sMODULARIZE -sEMBIND_STD_STRING_IS_UTF8 -sPTHREAD_POOL_DELAY_LOAD -sRUNTIME_DEBUG -sSTACK_OVERFLOW_CHECK=2 -sTEXTDECODER=2 -sPTHREAD_POOL_SIZE_STRICT=2 -sASSERTIONS=2 -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sDISABLE_EXCEPTION_CATCHING=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sPOLYFILL=0 -sEXPORTED_FUNCTIONS=_malloc -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sENVIRONMENT=web,worker -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -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 -pthread -flto --emit-tsd Vosklet.d.ts -fsanitize=undefined -fsanitize=address -fsanitize=leak -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals -g3 --pre-js pre.js -o ../Vosklet.js
em++ -O0 link.cc genericModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sMODULARIZE -sEMBIND_STD_STRING_IS_UTF8 -sPTHREAD_POOL_DELAY_LOAD -sRUNTIME_DEBUG -sSTACK_OVERFLOW_CHECK=2 -sTEXTDECODER=2 -sPTHREAD_POOL_SIZE_STRICT=2 -sASSERTIONS=2 -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sDISABLE_EXCEPTION_CATCHING=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sPOLYFILL=0 -sEXPORTED_FUNCTIONS=_malloc -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sENVIRONMENT=web,worker -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -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 -pthread -flto -fsanitize=undefined -fsanitize=address -fsanitize=leak -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals -g3 --pre-js pre.js -o ../test.js
elif [ $MODE = 1 ]; then
em++ -O3 link.cc genericModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sMODULARIZE -sEMBIND_STD_STRING_IS_UTF8 -sPTHREAD_POOL_DELAY_LOAD -sTEXTDECODER=2 -sPTHREAD_POOL_SIZE_STRICT=2 -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sSUPPORT_LONGJMP=0 -sEXPORTED_FUNCTIONS=_malloc -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sENVIRONMENT=web,worker -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -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 -pthread -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js pre.js -o ../Vosklet.js
em++ -O3 link.cc genericModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sMODULARIZE -sEMBIND_STD_STRING_IS_UTF8 -sPTHREAD_POOL_DELAY_LOAD -sTEXTDECODER=2 -sPTHREAD_POOL_SIZE_STRICT=2 -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sSUPPORT_LONGJMP=0 -sEXPORTED_FUNCTIONS=_malloc -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sENVIRONMENT=web,worker -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -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 -pthread -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js pre.js -o ../test.js
else
em++ -O0 link.cc genericModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sMODULARIZE -sEMBIND_STD_STRING_IS_UTF8 -sPTHREAD_POOL_DELAY_LOAD -sTEXTDECODER=2 -sPTHREAD_POOL_SIZE_STRICT=2 -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sSUPPORT_LONGJMP=0 -sEXPORTED_FUNCTIONS=_malloc -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sENVIRONMENT=web,worker -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -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 -pthread -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js pre.js -o ../Vosklet.js
em++ -O0 link.cc genericModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sMODULARIZE -sEMBIND_STD_STRING_IS_UTF8 -sPTHREAD_POOL_DELAY_LOAD -sTEXTDECODER=2 -sPTHREAD_POOL_SIZE_STRICT=2 -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sSUPPORT_LONGJMP=0 -sEXPORTED_FUNCTIONS=_malloc -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sENVIRONMENT=web,worker -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -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 -pthread -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js pre.js -o ../test.js
fi
rm -f Vosklet.worker.js