diff --git a/API.md b/API.md index a417f1e..f33d95d 100644 --- a/API.md +++ b/API.md @@ -47,7 +47,7 @@ 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*. +If you can't set them, you may use a hacky workaround in *AddCOI.js*. ## CSP headers Pthread worker construction must be from a blob (see [Emscripten issue](https://github.com/emscripten-core/emscripten/issues/21937)), so the CSP: @@ -57,7 +57,7 @@ Pthread worker construction must be from a blob (see [Emscripten issue](https:// Model response from ```fetch()``` must be an uncompressed model. Set your ```Content-Encoding``` response header and ```Accept-Encoding``` request header appropriately so browers can decompress. # Compilation -- Requires all Autotools commands in PATH, ```make```, and ```pkg-config```. Installing with ```apt```, for example: +- 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``` - Changing any option to non-default values requires recompilation @@ -70,7 +70,7 @@ cd Vosklet/src && ``` | 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, but can [affect performance](https://emscripten.org/docs/porting/pthreads.html#special-considerations:~:text=memory%20support%20available.-,Pthreads,-%2B%20memory%20growth%20(). | +| 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, but this may [affect performance](https://github.com/WebAssembly/design/issues/1271). | | MAX_THREADS | Set the max number of threads (>=1), this should be equal to the number of model and 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``` | diff --git a/AddCOI.js b/AddCOI.js new file mode 100644 index 0000000..0e7998f --- /dev/null +++ b/AddCOI.js @@ -0,0 +1,53 @@ +// Add cross-origin isolation (COI) into the page when the user visits it for the first time via a service worker and refreshing to apply the headers. +// Taken, and modified from https://github.com/orgs/community/discussions/13309#discussioncomment-3844940 +if(typeof window === 'undefined') { + self.addEventListener("install", () => self.skipWaiting()); + self.addEventListener("activate", e => e.waitUntil(self.clients.claim())); + async function handleFetch(request) { + if(request.cache === "only-if-cached" && request.mode !== "same-origin") return; + if(request.mode === "no-cors") { + request = new Request(request.url, { + cache: request.cache, + credentials: "omit", + headers: request.headers, + integrity: request.integrity, + destination: request.destination, + keepalive: request.keepalive, + method: request.method, + mode: request.mode, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + signal: request.signal, + }); + } + let r = await fetch(request).catch(e => console.error(e)); + if(r.status === 0) { + return r; + } + const headers = new Headers(r.headers); + headers.set("Cross-Origin-Embedder-Policy", "require-corp"); + headers.set("Cross-Origin-Opener-Policy", "same-origin"); + return new Response(r.body, { status: r.status, statusText: r.statusText, headers }); + } + + self.addEventListener("fetch", function(e) { + e.respondWith(handleFetch(e.request)); + }); +} else { + (async function() { + if(window.crossOriginIsolated !== false) return; + + let registration = await navigator.serviceWorker.register(window.document.currentScript.src).catch(e => console.error("COOP/COEP Service Worker failed to register:", e)); + if(registration) { + + registration.addEventListener("updatefound", () => { + window.location.reload(); + }); + + if(registration.active && !navigator.serviceWorker.controller) { + window.location.reload(); + } + } + })(); +} \ No newline at end of file diff --git a/examples/README.md b/Examples/README.md similarity index 100% rename from examples/README.md rename to Examples/README.md diff --git a/examples/Vosklet.js b/Examples/Vosklet.js similarity index 100% rename from examples/Vosklet.js rename to Examples/Vosklet.js diff --git a/examples/example.wav b/Examples/example.wav similarity index 100% rename from examples/example.wav rename to Examples/example.wav diff --git a/examples/fromMic.html b/Examples/fromMic.html similarity index 100% rename from examples/fromMic.html rename to Examples/fromMic.html diff --git a/examples/fromWav.html b/Examples/fromWav.html similarity index 100% rename from examples/fromWav.html rename to Examples/fromWav.html diff --git a/addCOI.js b/addCOI.js deleted file mode 100644 index a8dece5..0000000 --- a/addCOI.js +++ /dev/null @@ -1,55 +0,0 @@ -// Add cross-origin isolation (COI) into the page when the user visits it for the first time via a service worker and refreshing to apply the headers. -// Taken, and modified from https://github.com/orgs/community/discussions/13309#discussioncomment-3844940 -if(typeof window === 'undefined') { - self.addEventListener("install", () => self.skipWaiting()); - self.addEventListener("activate", e => e.waitUntil(self.clients.claim())); - async function handleFetch(request) { - if(request.cache === "only-if-cached" && request.mode !== "same-origin") { - return; - } - if(request.mode === "no-cors") { - request = new Request(request.url, { - cache: request.cache, - credentials: "omit", - headers: request.headers, - integrity: request.integrity, - destination: request.destination, - keepalive: request.keepalive, - method: request.method, - mode: request.mode, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - signal: request.signal, - }); - } - let r = await fetch(request).catch(e => console.error(e)); - if(r.status === 0) { - return r; - } - const headers = new Headers(r.headers); - headers.set("Cross-Origin-Embedder-Policy", "require-corp"); - headers.set("Cross-Origin-Opener-Policy", "same-origin"); - return new Response(r.body, { status: r.status, statusText: r.statusText, headers }); - } - - self.addEventListener("fetch", function(e) { - e.respondWith(handleFetch(e.request)); - }); - } else { - (async function() { - if(window.crossOriginIsolated !== false) return; - - let registration = await navigator.serviceWorker.register(window.document.currentScript.src).catch(e => console.error("COOP/COEP Service Worker failed to register:", e)); - if(registration) { - - registration.addEventListener("updatefound", () => { - window.location.reload(); - }); - - if(registration.active && !navigator.serviceWorker.controller) { - window.location.reload(); - } - } - })(); - } \ No newline at end of file diff --git a/src/Bindings.cc b/src/Bindings.cc new file mode 100644 index 0000000..a3b2b1c --- /dev/null +++ b/src/Bindings.cc @@ -0,0 +1,34 @@ +#include "CommonModel.h" +#include "Recognizer.h" + +#include +using namespace emscripten; + +EMSCRIPTEN_BINDINGS() { + function("setLogLevel", &vosk_set_log_level, allow_raw_pointers()); + + enum_("EpMode") + .value("ANSWER_DEFAULT", VOSK_EP_ANSWER_DEFAULT) + .value("ANSWER_SHORT", VOSK_EP_ANSWER_SHORT) + .value("ANSWER_LONG", VOSK_EP_ANSWER_LONG) + .value("ANSWER_VERY_LONG", VOSK_EP_ANSWER_VERY_LONG); + + class_("CommonModel") + .constructor(allow_raw_pointers()) + .function("findWord", &CommonModel::findWord, allow_raw_pointers()); + + class_("Recognizer") + .constructor(allow_raw_pointers()) + .constructor(allow_raw_pointers()) + .constructor(allow_raw_pointers()) + .function("pushData", &Recognizer::pushData, allow_raw_pointers()) + .function("reset", &Recognizer::reset, allow_raw_pointers()) + .function("setEndpointerMode", &Recognizer::setEndpointerMode, allow_raw_pointers()) + .function("setEndpointerDelays", &Recognizer::setEndpointerDelays, allow_raw_pointers()) + .function("setWords", &Recognizer::setWords, allow_raw_pointers()) + .function("setPartialWords", &Recognizer::setPartialWords, allow_raw_pointers()) + .function("setGrm", &Recognizer::setGrm, allow_raw_pointers()) + .function("setNLSML", &Recognizer::setNLSML, allow_raw_pointers()) + .function("setSpkModel", &Recognizer::setSpkModel, allow_raw_pointers()) + .function("setMaxAlternatives", &Recognizer::setMaxAlternatives, allow_raw_pointers()); +}; \ No newline at end of file diff --git a/src/clapack-wasm.patch b/src/Clapack-wasm.patch similarity index 100% rename from src/clapack-wasm.patch rename to src/Clapack-wasm.patch diff --git a/src/CommonModel.cc b/src/CommonModel.cc new file mode 100644 index 0000000..e136d41 --- /dev/null +++ b/src/CommonModel.cc @@ -0,0 +1,39 @@ +#include "CommonModel.h" + +CommonModel::CommonModel(int index, bool normalMdl, std::string storepath, std::string id, int tarStart, int tarSize) : normalMdl{normalMdl}, index{index}, storepath{std::move(storepath)}, id{std::move(id)} { + t = std::thread{extractAndLoad, reinterpret_cast(tarStart), tarSize}; +} +void CommonModel::extractAndLoad(unsigned char* tar, int tarSize) { + int res{untar(tar, tarSize, storepath)}; + free(tar); + switch(res) { + case IncorrectFormat: + fireEv(index, "Untar: Incorrect tar format, must be USTAR"); + return; + case IncorrectFiletype: + fireEv(index, "Untar: Not a directory or regular file"); + return; + case FailedOpen: + fireEv(index, "Untar: Unable to open file for write"); + return; + case FailedWrite: + fireEv(index, "Untar: Unable to write file"); + return; + case FailedClose: + fireEv(index, "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(mdl) != nullptr : std::get(mdl) != nullptr) fireEv(index, "0"); + else fireEv(index, "Unable to load model for recognition"); + fs::remove_all(storepath); +} +int CommonModel::findWord(std::string word) { + return vosk_model_find_word(std::get(mdl), word.c_str()); +} +CommonModel::~CommonModel() { + if(normalMdl) vosk_model_free(std::get(mdl)); + else vosk_spk_model_free(std::get(mdl)); + if(t.joinable()) t.join(); +} \ No newline at end of file diff --git a/src/CommonModel.h b/src/CommonModel.h new file mode 100644 index 0000000..694414e --- /dev/null +++ b/src/CommonModel.h @@ -0,0 +1,21 @@ +#pragma once +#include "Util.h" + +#include + +extern void free(void*); +struct CommonModel { + bool normalMdl; + bool thrdUsed{}; + int index; + std::thread t; + std::string storepath; + std::string id; + std::variant 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); + ~CommonModel(); +}; + diff --git a/src/kaldi.patch b/src/Kaldi.patch similarity index 100% rename from src/kaldi.patch rename to src/Kaldi.patch diff --git a/src/openfst.patch b/src/Openfst.patch similarity index 100% rename from src/openfst.patch rename to src/Openfst.patch diff --git a/src/recognizer.cc b/src/Recognizer.cc similarity index 60% rename from src/recognizer.cc rename to src/Recognizer.cc index 317c517..a17ba86 100644 --- a/src/recognizer.cc +++ b/src/Recognizer.cc @@ -1,57 +1,60 @@ #include "recognizer.h" -recognizer::recognizer(int index, float sampleRate, genericModel* model) : index{index}, rec{vosk_recognizer_new(std::get(model->mdl),sampleRate)} { +recognizer::recognizer(int index, float sampleRate, CommonModel* model) : index{index}, rec{vosk_recognizer_new(std::get(model->mdl),sampleRate)}, t{std::move(model->t)} { finishConstruction(model); } -recognizer::recognizer(int index, float sampleRate, genericModel* model, genericModel* spkModel) : index{index}, rec{vosk_recognizer_new_spk(std::get(model->mdl), sampleRate, std::get(spkModel->mdl))} { +recognizer::recognizer(int index, float sampleRate, CommonModel* model, CommonModel* spkModel) : index{index}, rec{vosk_recognizer_new_spk(std::get(model->mdl), sampleRate, std::get(spkModel->mdl))}, t{std::move(model->t)} { finishConstruction(model, spkModel); } -recognizer::recognizer(int index, float sampleRate, genericModel* model, const std::string& grm, int) : index{index}, rec{vosk_recognizer_new_grm(std::get(model->mdl), sampleRate, grm.c_str())} { +recognizer::recognizer(int index, float sampleRate, CommonModel* model, const std::string& grm, int) : index{index}, rec{vosk_recognizer_new_grm(std::get(model->mdl), sampleRate, grm.c_str())}, t{std::move(model->t)} { finishConstruction(model); } recognizer::~recognizer() { done = true; vosk_recognizer_free(rec); + if(t.joinable()) t.join(); } -void recognizer::finishConstruction(genericModel* model, genericModel* spkModel) { +void recognizer::finishConstruction(CommonModel* model, CommonModel* spkModel) { if(rec == nullptr) { fireEv(index, "Unable to initialize recognizer"); return; } auto main {[this](){ fireEv(index, "0"); + audioData* next; while(!done) { blocker.wait(done, std::memory_order_relaxed); blocker = false; + next = &dataQ.front(); while(!dataQ.empty()) { - switch(vosk_recognizer_accept_waveform_f(rec, dataQ.front().data, dataQ.front().len)) { + switch(vosk_recognizer_accept_waveform_f(rec, next->data, next->len)) { case 0: fireEv(index, vosk_recognizer_result(rec), "result"); break; case 1: fireEv(index, vosk_recognizer_partial_result(rec), "partialResult"); } - free(dataQ.front().data); + free(next->data); dataQ.pop(); } } }}; - if(!model->resourceUsed) { - model->resourceUsed = true; + if(!model->thrdUsed) { + model->thrdUsed = true; model->func = main; model->blocker = true; model->blocker.notify_one(); return; } - if(spkModel != nullptr && !spkModel->resourceUsed) { - spkModel->resourceUsed = true; + if(spkModel != nullptr && !spkModel->thrdUsed) { + spkModel->thrdUsed = true; spkModel->func = main; spkModel->blocker = true; model->blocker.notify_one(); return; } - std::thread t{main}; - t.detach(); + blocker.is_always_lock_free + std::thread{main}.detach(); } void recognizer::pushData(int start, int len) { dataQ.emplace(start, len); @@ -70,11 +73,11 @@ void recognizer::setEndpointerDelays(float tStartMax, float tEnd, float tMax) { void recognizer::setGrm(const std::string& grm) { vosk_recognizer_set_grm(rec, grm.c_str()); } -void recognizer::setSpkModel(genericModel* spkModel) { +void recognizer::setSpkModel(CommonModel* spkModel) { vosk_recognizer_set_spk_model(rec, std::get(spkModel->mdl)); } void recognizer::setWords(bool words) { - vosk_recognizer_set_words(rec,words); + vosk_recognizer_set_words(rec, words); } void recognizer::setPartialWords(bool partialWords) { vosk_recognizer_set_partial_words(rec, partialWords); diff --git a/src/recognizer.h b/src/Recognizer.h similarity index 57% rename from src/recognizer.h rename to src/Recognizer.h index 8a5bf37..b29abba 100644 --- a/src/recognizer.h +++ b/src/Recognizer.h @@ -1,5 +1,5 @@ #pragma once -#include "genericModel.h" +#include "CommonModel.h" #include @@ -10,22 +10,23 @@ struct audioData { int len; audioData(int start, int len) : data{reinterpret_cast(start)}, len{len} {} }; -struct recognizer { +struct Recognizer { std::atomic_bool done{}; std::atomic_bool blocker{}; int index; + std::thread t; VoskRecognizer* rec; - std::queue dataQ{}; - recognizer(int index, float sampleRate, genericModel* model); - recognizer(int index, float sampleRate, genericModel* model, genericModel* spkModel); - recognizer(int index, float sampleRate, genericModel* model, const std::string& grm, int); - ~recognizer(); - void finishConstruction(genericModel* model, genericModel* spkModel = nullptr); + std::queue 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 pushData(int start, int len); void reset(); void setEndpointerMode(VoskEndpointerMode mode); void setEndpointerDelays(float tStartMax, float tEnd, float tMax); - void setSpkModel(genericModel* model); + void setSpkModel(CommonModel* model); void setGrm(const std::string& grm); void setWords(bool words); void setPartialWords(bool partialWords); diff --git a/src/util.cc b/src/Util.cc similarity index 82% rename from src/util.cc rename to src/Util.cc index a0976f4..8792e33 100644 --- a/src/util.cc +++ b/src/Util.cc @@ -1,12 +1,11 @@ -#include "util.h" - +#include "Util.h" void fireEv(int index, const char* content, const char* type) { MAIN_THREAD_EM_ASM({ objs[$0].dispatchEvent(new CustomEvent($2 === 0 ? "0" : UTF8ToString($2), { "detail" : UTF8ToString($1) })); }, index, content, type); } int untar(unsigned char* tar, int tarSize, const std::string& storepath) { - if(memcmp(tar + 257, "ustar", 5)) return 1; + if(memcmp(tar + 257, "ustar", 5)) return IncorrectFormat; unsigned char* ptr = tar; size_t size{0}; std::string path{}; @@ -15,7 +14,7 @@ int untar(unsigned char* tar, int tarSize, const std::string& storepath) { while(ptr <= end) { if(ptr[156] != '5' && ptr[156] != 0 && ptr[156] != '0') { - return 2; + return IncorrectFiletype; } path.clear(); path += reinterpret_cast(ptr + 345); @@ -36,12 +35,12 @@ int untar(unsigned char* tar, int tarSize, const std::string& storepath) { if(size == 0) fs::create_directory(path); else { int fd {open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0777)}; - if(fd == -1) return 3; + if(fd == -1) return FailedOpen; int res = write(fd, ptr, size); - if(res == -1) return 4; - if(close(fd) == -1) return 5; + if(res == -1) return FailedWrite; + if(close(fd) == -1) return FailedClose; ptr += size + 512 - size % 512; } } - return 0; + return Successful; } \ No newline at end of file diff --git a/src/util.h b/src/Util.h similarity index 75% rename from src/util.h rename to src/Util.h index ff60cfe..4d30b31 100644 --- a/src/util.h +++ b/src/Util.h @@ -10,6 +10,13 @@ #include namespace fs = std::filesystem; - +enum UntarStatus { + Successful, + IncorrectFormat, + IncorrectFiletype, + FailedOpen, + FailedWrite, + FailedClose +}; void fireEv(int index, const char* content, const char* type = nullptr); int untar(unsigned char* tar, int tarSize, const std::string& storepath); \ No newline at end of file diff --git a/src/vosk.patch b/src/Vosk.patch similarity index 100% rename from src/vosk.patch rename to src/Vosk.patch diff --git a/src/wrapper.js b/src/Wrapper.js similarity index 85% rename from src/wrapper.js rename to src/Wrapper.js index af66215..c4cd44d 100644 --- a/src/wrapper.js +++ b/src/Wrapper.js @@ -44,25 +44,23 @@ async function getFileHandle(path, create = false) { return prevDir.getFileHandle(components[components.length - 1], { create : create }) } -class genericModel extends EventTarget { +class CommonModel extends EventTarget { constructor() { super() objs.push(this) } static async create(url, storepath, id, normalMdl) { - let mdl = new genericModel() + let mdl = new CommonModel() let result = new Promise((resolve, reject) => { mdl.addEventListener("0", ev => { if(ev.detail == "0") { if(normalMdl) mdl.findWord = (word) => mdl.obj.findWord(word) return resolve(mdl) } - mdl.delete() reject(ev.detail) }, { once : true }) }) let tar - mdl.obj = new Module.genericModel(objs.length - 1, normalMdl, "/" + storepath, id) try { let dataFile = await (await getFileHandle(storepath + "/model.tgz")).getFile() let idFile = await (await getFileHandle(storepath + "/id")).getFile() @@ -89,7 +87,7 @@ class genericModel extends EventTarget { } let tarStart = Module._malloc(tar.byteLength) Module.HEAPU8.set(new Uint8Array(tar), tarStart) - mdl.obj.extractAndLoad(tarStart, tar.byteLength) + mdl.obj = new Module.CommonModel(objs.length - 1, normalMdl, "/" + storepath, id, tarStart, tar.byteLength) return result } delete() { @@ -98,14 +96,14 @@ class genericModel extends EventTarget { } Module.createModel = async (url, storepath, id) => { - return genericModel.create(url, storepath, id, true) + return CommonModel.create(url, storepath, id, true) } Module.createSpkModel = async (url, storepath, id) => { - return genericModel.create(url, storepath, id, false) + return CommonModel.create(url, storepath, id, false) } -class recognizer extends EventTarget { +class Recognizer extends EventTarget { constructor() { super() objs.push(this) @@ -116,7 +114,7 @@ class recognizer extends EventTarget { }) } static async create(model, sampleRate, mode, grammar, spkModel) { - let rec = new recognizer() + let rec = new Recognizer() let result = new Promise((resolve, reject) => { rec.addEventListener("0", ev => { if(ev.detail == "0") return resolve(rec) @@ -126,13 +124,13 @@ class recognizer extends EventTarget { }) switch(mode) { case 1: - rec.obj = new Module.recognizer(objs.length - 1, sampleRate, model) + rec.obj = new Module.Recognizer(objs.length - 1, sampleRate, model) break case 2: - rec.obj = new Module.recognizer(objs.length -1, sampleRate, model, spkModel) + rec.obj = new Module.Recognizer(objs.length -1, sampleRate, model, spkModel) break default: - rec.obj = new Module.recognizer(objs.length - 1, sampleRate, model, grammar, 0) + rec.obj = new Module.Recognizer(objs.length - 1, sampleRate, model, grammar, 0) } return result } @@ -147,15 +145,15 @@ class recognizer extends EventTarget { } Module.createRecognizer = (model, sampleRate) => { - return recognizer.create(model.obj, sampleRate, 1) + return Recognizer.create(model.obj, sampleRate, 1) } Module.createRecognizerWithSpkModel = (model, sampleRate, spkModel) => { - return recognizer.create(model.obj, sampleRate, 2, null, spkModel.obj) + return Recognizer.create(model.obj, sampleRate, 2, null, spkModel.obj) } Module.createRecognizerWithGrm = (model, sampleRate, grammar) => { - return recognizer.create(model.obj, sampleRate, 3, grammar, null) + return Recognizer.create(model.obj, sampleRate, 3, grammar, null) } // See Emscripten issue #21937 diff --git a/src/bindings.cc b/src/bindings.cc deleted file mode 100644 index fd48d71..0000000 --- a/src/bindings.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include "genericModel.h" -#include "recognizer.h" - -#include -using namespace emscripten; - -EMSCRIPTEN_BINDINGS() { - function("setLogLevel", &vosk_set_log_level, allow_raw_pointers()); - - enum_("EpMode") - .value("ANSWER_DEFAULT", VOSK_EP_ANSWER_DEFAULT) - .value("ANSWER_SHORT", VOSK_EP_ANSWER_SHORT) - .value("ANSWER_LONG", VOSK_EP_ANSWER_LONG) - .value("ANSWER_VERY_LONG", VOSK_EP_ANSWER_VERY_LONG); - - class_("genericModel") - .constructor(allow_raw_pointers()) - .function("extractAndLoad", &genericModel::extractAndLoad, allow_raw_pointers()) - .function("findWord", &genericModel::findWord, allow_raw_pointers()); - - class_("recognizer") - .constructor(allow_raw_pointers()) - .constructor(allow_raw_pointers()) - .constructor(allow_raw_pointers()) - .function("pushData", &recognizer::pushData, allow_raw_pointers()) - .function("reset", &recognizer::reset, allow_raw_pointers()) - .function("setEndpointerMode", &recognizer::setEndpointerMode, allow_raw_pointers()) - .function("setEndpointerDelays", &recognizer::setEndpointerDelays, allow_raw_pointers()) - .function("setWords", &recognizer::setWords, allow_raw_pointers()) - .function("setPartialWords", &recognizer::setPartialWords, allow_raw_pointers()) - .function("setGrm", &recognizer::setGrm, allow_raw_pointers()) - .function("setNLSML", &recognizer::setNLSML, allow_raw_pointers()) - .function("setSpkModel", &recognizer::setSpkModel, allow_raw_pointers()) - .function("setMaxAlternatives", &recognizer::setMaxAlternatives, allow_raw_pointers()); -}; \ No newline at end of file diff --git a/src/genericModel.cc b/src/genericModel.cc deleted file mode 100644 index 05ba7bf..0000000 --- a/src/genericModel.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "genericModel.h" - -genericModel::genericModel(int index, bool normalMdl, std::string storepath, std::string id) : normalMdl{normalMdl}, index{index}, storepath{std::move(storepath)}, id{std::move(id)} {} -void genericModel::extractAndLoad(int tarStart, int tarSize) { - func = [this, tar = reinterpret_cast(tarStart), tarSize]() { - int res{untar(tar, tarSize, storepath)}; - free(tar); - switch(res) { - case 1: - fireEv(index, "Untar: Incorrect tar format, must be USTAR"); - return; - case 2: - fireEv(index, "Untar: Not a directory or regular file"); - return; - case 3: - fireEv(index, "Untar: Unable to open file for write"); - return; - case 4: - fireEv(index, "Untar: Unable to write file"); - return; - case 5: - fireEv(index, "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(mdl) != nullptr : std::get(mdl) != nullptr) fireEv(index, "0"); - else fireEv(index, "Unable to load model for recognition"); - fs::remove_all(storepath); - }; - std::thread t{[this](){ - func(); - blocker.wait(false, std::memory_order_relaxed); - func(); - }}; - t.detach(); -} -int genericModel::findWord(std::string word) { - return vosk_model_find_word(std::get(mdl), word.c_str()); -} -genericModel::~genericModel() { - if(normalMdl) vosk_model_free(std::get(mdl)); - else vosk_spk_model_free(std::get(mdl)); -} \ No newline at end of file diff --git a/src/genericModel.h b/src/genericModel.h deleted file mode 100644 index 9e430c4..0000000 --- a/src/genericModel.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "util.h" - -#include - -extern void free(void*); -struct genericModel { - bool normalMdl; - bool resourceUsed{}; - std::atomic_bool blocker{}; - int index; - std::string storepath; - std::string id; - std::variant mdl; - std::function func; - void extractAndLoad(int tarStart, int tarSize); - int findWord(std::string word); - genericModel(int index, bool normalMdl, std::string storepath, std::string id); - ~genericModel(); -}; - diff --git a/src/make b/src/make index 34ee540..55fa285 100755 --- a/src/make +++ b/src/make @@ -41,7 +41,7 @@ if [ ! -d $OPENFST ]; then rm -rf /tmp/openfst && git clone --depth=1 https://github.com/alphacep/openfst /tmp/openfst && cd /tmp/openfst && - git apply $SRC/openfst.patch + git apply $SRC/Openfst.patch autoreconf -is && CXXFLAGS="-r -O3 -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals" LDFLAGS="-O3 -flto" emconfigure ./configure --prefix=$OPENFST --enable-static --disable-shared --enable-lookahead-fsts --enable-ngram-fsts --disable-bin && emmake make -j$JOBS install && @@ -51,14 +51,14 @@ fi if [ ! -d $CLAPACK_WASM ]; then git clone --depth=1 https://gitlab.inria.fr/multispeech/kaldi.web/clapack-wasm.git $CLAPACK_WASM && cd $CLAPACK_WASM && - git apply $SRC/clapack-wasm.patch && + git apply $SRC/Clapack-wasm.patch && bash install_repo.sh emcc fi if [ ! -d $KALDI ]; then git clone -b vosk --depth=1 https://github.com/alphacep/kaldi $KALDI && cd $KALDI/src && - git apply $SRC/kaldi.patch && + git apply $SRC/Kaldi.patch && CXXFLAGS="-O3 -UHAVE_EXECINFO_H -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals -Wno-unused-variable -Wno-unused-but-set-variable -g0" LDFLAGS="-O3 -lembind -flto -g0" emconfigure ./configure --use-cuda=no --with-cudadecoder=no --static --static-math=yes --static-fst=yes --debug-level=0 --fst-root=$OPENFST --clapack-root=$CLAPACK_WASM --host=WASM && emmake make -j$JOBS online2 rnnlm fi @@ -66,14 +66,14 @@ fi if [ ! -d $VOSK ]; then git clone -b v0.3.50 --depth=1 https://github.com/alphacep/vosk-api $VOSK && cd $VOSK/src && - git apply $SRC/vosk.patch && - VOSK_FILES="recognizer.cc language_model.cc model.cc spk_model.cc vosk_api.cc" && + git apply $SRC/Vosk.patch && + VOSK_FILES="Recognizer.cc language_model.cc model.cc spk_model.cc vosk_api.cc" && em++ -O3 -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals -Wno-deprecated -I. -I$KALDI/src -I$OPENFST/include $VOSK_FILES -c && emar -rcs vosk.a ${VOSK_FILES//.cc/.o} fi cd $SRC && -em++ util.cc genericModel.cc recognizer.cc bindings.cc -O3 -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sMODULARIZE -sEMBIND_STD_STRING_IS_UTF8 -sPTHREAD_POOL_DELAY_LOAD -sALLOW_MEMORY_GROWTH -sTEXTDECODER=2 -sPTHREAD_POOL_SIZE_STRICT=2 -sINITIAL_MEMORY=$INITIAL_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"$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 -pthread -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js wrapper.js -o ../Vosklet.js && +em++ util.cc CommonModel.cc Recognizer.cc Bindings.cc -O3 -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sMODULARIZE -sEMBIND_STD_STRING_IS_UTF8 -sPTHREAD_POOL_DELAY_LOAD -sALLOW_MEMORY_GROWTH -sTEXTDECODER=2 -sPTHREAD_POOL_SIZE_STRICT=2 -sINITIAL_MEMORY=$INITIAL_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"$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 -pthread -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js Wrapper.js -o ../Vosklet.js && cd .. && rm -f Vosklet.worker.js