From c20efbeb2901444c1c59022b8a5600ef328937a0 Mon Sep 17 00:00:00 2001 From: msqr1 Date: Mon, 29 Jan 2024 22:44:09 -0800 Subject: [PATCH] Design structure change again........ OMG STOPPP --- src/bindings.cc | 16 ++++----- src/genericModel.cc | 39 +++++++++++++-------- src/genericModel.h | 8 +++-- src/global.cc | 35 ++++++++++--------- src/global.h | 3 +- src/model.cc | 15 +++++--- src/model.h | 7 ++-- src/pre.js | 83 +++++++++++++++++++++++++++------------------ src/recognizer.cc | 45 ++++++++++-------------- src/recognizer.h | 4 +-- src/spkModel.cc | 15 +++++--- src/spkModel.h | 10 +++--- 12 files changed, 153 insertions(+), 127 deletions(-) diff --git a/src/bindings.cc b/src/bindings.cc index 0f5faf2..9dff633 100644 --- a/src/bindings.cc +++ b/src/bindings.cc @@ -5,16 +5,14 @@ using namespace emscripten; EMSCRIPTEN_BINDINGS() { function("setLogLevel", &vosk_set_log_level, allow_raw_pointers()); - class_("Model") - .constructor(allow_raw_pointers()) - .function("checkModelFiles", &model::checkModelFiles, allow_raw_pointers()) - .function("checkModelId", &model::checkModelId, allow_raw_pointers()) - .function("afterFetch", &model::afterFetch, allow_raw_pointers()); + class_("model") + .constructor(allow_raw_pointers()) + .function("checkModel", &spkModel::checkModel, allow_raw_pointers()) + .function("afterFetch", &spkModel::afterFetch, allow_raw_pointers()); - class_("SpkModel") - .constructor(allow_raw_pointers()) - .function("checkModelFiles", &spkModel::checkModelFiles, allow_raw_pointers()) - .function("checkModelId", &spkModel::checkModelId, allow_raw_pointers()) + class_("spkModel") + .constructor(allow_raw_pointers()) + .function("checkModel", &spkModel::checkModel, allow_raw_pointers()) .function("afterFetch", &spkModel::afterFetch, allow_raw_pointers()); class_("recognizer") diff --git a/src/genericModel.cc b/src/genericModel.cc index e8a512f..b1dc939 100644 --- a/src/genericModel.cc +++ b/src/genericModel.cc @@ -1,10 +1,11 @@ #include "genericModel.h" -genericModel::genericModel(const std::string& storepath, const std::string &id) : storepath(storepath), id(id) { +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); } -bool genericModel::checkModelId() { +bool genericModel::checkModel() { + if(!checkModelFiles()) return false; static std::error_code c{}; if(!fs::exists("id", c)) return false; std::ifstream file {"id", std::ifstream::binary}; @@ -15,18 +16,28 @@ bool genericModel::checkModelId() { file.read(&oldid[0], size); return id.compare(oldid) == 0 ? true : false; } -bool genericModel::afterFetch(int memAddr, size_t size) { - if(!extractModel(reinterpret_cast(memAddr), size)) { - return false; - } - std::ofstream idFile("id"); - if(!idFile.is_open()) { - fs::current_path("/opfs"); - fs::remove_all(storepath); - return false; - } - idFile << id; - return true; +void genericModel::afterFetch(int memAddr, size_t size) { + // FIXME: Recognizer can reuse this thread to avoid respawning threads + std::thread t{[this, memAddr, size](){ + char* modelData = reinterpret_cast(memAddr); + if(!extractModel(modelData, size)) { + free(modelData); + fireEv("_continue", "Unable to extract model", index); + return; + } + free(modelData); + std::ofstream idFile("id"); + if(!idFile.is_open()) { + fs::current_path("/opfs"); + fs::remove_all(storepath); + fireEv("_continue", "Unable to write model ID", index); + return; + } + idFile << id; + if(!load()) + fireEv("_continue", ".", index); + }}; + t.detach(); } bool genericModel::extractModel(const char* fileBuf, size_t size) { std::string path{}; diff --git a/src/genericModel.h b/src/genericModel.h index 3f40b00..1d4f39c 100644 --- a/src/genericModel.h +++ b/src/genericModel.h @@ -14,10 +14,12 @@ namespace fs = std::filesystem; struct genericModel { const std::string storepath{}; const std::string id{}; + int index{}; static bool extractModel(const char* fileBuf, size_t size); virtual bool checkModelFiles() = 0; - bool checkModelId(); - bool afterFetch(int memAddr, size_t size); - genericModel(const std::string &storepath, const std::string &id); + virtual bool load() = 0; + bool checkModel(); + void afterFetch(int memAddr, size_t size); + genericModel(const std::string &storepath, const std::string &id, int index); }; diff --git a/src/global.cc b/src/global.cc index 9279950..5194419 100644 --- a/src/global.cc +++ b/src/global.cc @@ -1,27 +1,26 @@ #include "global.h" -// Throw error for user, or just throw the message for internal communication + void throwJS(const char* msg, bool err) { - static pthread_t targetThrd{pthread_self()}; + EM_ASM({ + if($1) { + throw Error(UTF8ToString($0)); + return; + } + throw UTF8ToString($0); + },msg, err); +} +void fireEv(const char *type, const char *content, int index) { static ProxyingQueue pq{}; - if(pthread_self() == targetThrd) { + static pthread_t selfTID {pthread_self()}; + pq.proxySync(selfTID, [&](){ EM_ASM({ - if($1) { - throw Error(UTF8ToString($0)); - return; - } - throw UTF8ToString($0); - },msg, err); - } - pq.proxyAsync(targetThrd, [&](){ - EM_ASM({ - if($1) { - throw Error(UTF8ToString($0)); - return; - } - throw UTF8ToString($0); - },msg, err); + let ev = new CustomEvent(UTF8ToString($1), {"details" : UTF8ToString($2)}); + objs[$0].dispatchEvent(ev); + console.log(objs[$0], ev) + },index, type, content); }); } + int main() { //vosk_set_log_level(-1); std::thread t{[](){ diff --git a/src/global.h b/src/global.h index 933dac9..5b79164 100644 --- a/src/global.h +++ b/src/global.h @@ -6,7 +6,8 @@ #include #include #include -using namespace emscripten; +using namespace emscripten; void throwJS(const char* msg, bool err = false); +void fireEv(const char *type, const char *content, int index); int main(); diff --git a/src/model.cc b/src/model.cc index ed4f633..a3aa7d4 100644 --- a/src/model.cc +++ b/src/model.cc @@ -1,14 +1,19 @@ #include "model.h" -model::model(const std::string& storepath, const std::string& id) : genericModel(storepath, id) {} +model::model(const std::string& storepath, const std::string& id, int index) : genericModel(storepath, id, index) {} model::~model() { vosk_model_free(mdl); } -bool model::checkModelId() { - return genericModel::checkModelId(); +void model::afterFetch(int addr, size_t size) { + genericModel::afterFetch(addr,size); } -bool model::afterFetch(int addr, size_t size) { - return genericModel::afterFetch(addr,size); +void model::checkModel() { + genericModel::checkModel(); +} +bool model::load() { + mdl = vosk_model_new(storepath.c_str()); + if(mdl == nullptr) return false; + return true; } bool model::checkModelFiles() { static std::error_code c{}; diff --git a/src/model.h b/src/model.h index 7d48cdd..876ed2f 100644 --- a/src/model.h +++ b/src/model.h @@ -4,9 +4,10 @@ struct model : genericModel { bool checkModelFiles(); VoskModel* mdl{}; - model(const std::string& storepath, const std::string& id); - bool checkModelId(); - bool afterFetch(int addr, size_t size); + model(const std::string& storepath, const std::string& id, int index); + void checkModel(); + void afterFetch(int addr, size_t size); + bool load(); ~model(); }; diff --git a/src/pre.js b/src/pre.js index 2cb71fc..8856f56 100644 --- a/src/pre.js +++ b/src/pre.js @@ -1,8 +1,10 @@ let objs = [] class Recognizer extends EventTarget { - constructor(rec) { + constructor() { super() - this.obj = rec + } + _init(model, sampleRate) { + this.obj = new Module.recognizer(model, sampleRate, objs.length) objs.push(this) this.ptr = Module._malloc(512) } @@ -47,48 +49,64 @@ class Recognizer extends EventTarget { this.obj.setMaxAlternatives(alts) } } +class Model extends EventTarget { + constructor() { + super() + } + _init(url, storepath) { + this.obj = new Module.model(url, storepath, objs.length) + objs.push(this) + } +} Module.deleteAll = () => { objs.forEach(obj => obj.delete()) } Module.makeModel = async (url, storepath, id) => { - let mdl = new Module.Model(storepath, id) - let mdlMem; - if(mdl.checkModelFiles() && mdl.checkModelId()) { - objs.push(mdl) - return mdl - } + let mdl = new Model() + mdl.obj(new Module.model(url, storepath, objs.length)) + objs.push(mdl) + let retval = new Promise((resolve, reject) => { + rec.addEventListener("_continue", (ev) => { + if(mdl.checkModel()) { + + } + if(ev.details === ".") { + resolve(mdl) + } + reject(ev.details) + }, {once : true}) + + }) + let mdlMem try { + let res = await fetch(url) if(!res.ok) throw res.statusText let arr = await res.arrayBuffer() - mdlMem = Module._malloc(arr.byteLength) + mdlMem = Module._malloc(arr.byteLength) // Will free in C++ Module.HEAP8.set(new Int8Array(arr), mdlMem) - if(!mdl.afterFetch(mdlMem, arr.byteLength)) throw "Unable to extract model and write ID" - if(!mdl.checkModelFiles()) throw "Model contains invalid model files" + mdl.afterFetch(mdlMem, arr.byteLength) } catch(e) { mdl.delete() return Promise.reject(e.message || e) } - finally { - Module._free(mdlMem) - } objs.push(mdl) return mdl } Module.makeSpkModel = async (url, storepath, id) => { let mdl = new Module.SpkModel(storepath, id) - let mdlMem; - if(mdl.checkModelFiles() && mdl.checkModelId()) { - objs.push(mdl) - return mdl - } + let mdlMem try { + if(mdl.checkModelFiles() && mdl.checkModelId()) { + objs.push(mdl) + return mdl + } let res = await fetch(url) if(!res.ok) throw res.statusText let arr = await res.arrayBuffer() mdlMem = Module._malloc(arr.byteLength) - Module.HEAP8.set(new Int8Array(arr), mdlMem) + Module.HEAP8.set(new Int8Array(arr), mdlMem) if(!mdl.afterFetch(mdlMem, arr.byteLength)) throw "Unable to extract model and write ID" if(!mdl.checkModelFiles()) throw "Model contains invalid model files" } @@ -96,20 +114,19 @@ Module.makeSpkModel = async (url, storepath, id) => { mdl.delete() return Promise.reject(e.message || e) } - finally { - Module._free(mdlMem) - } objs.push(mdl) return mdl } -Module.makeRecognizer = async (model, sampleRate) => { - let rec - try { - rec = new Module.recognizer(model, sampleRate, objs.length) - } - catch(e) { - rec.delete() - return Promise.reject(e) - } - return new Recognizer(rec) +Module.makeRecognizer = (model, sampleRate) => { + let rec = new Recognizer() + let retval = new Promise((resolve, reject) => { + rec.addEventListener("_continue", (ev) => { + if(ev.details == ".") { + resolve(rec) + } + reject(ev.details) + }, {once : true}) + }) + rec._init(model.obj, sampleRate) + return retval } diff --git a/src/recognizer.cc b/src/recognizer.cc index 25628ce..0cc6ab9 100644 --- a/src/recognizer.cc +++ b/src/recognizer.cc @@ -1,30 +1,22 @@ #include "recognizer.h" recognizer::recognizer(model* mdl, float sampleRate, int index) : index(index) { - fs::current_path("/opfs"); - fs::current_path(mdl->storepath); std::thread t{[this](VoskModel* mdl, VoskRecognizer* rec, float sampleRate){ - if(mdl == nullptr) { - mdl = vosk_model_new("."); - if(mdl == nullptr) { - throwJS("Unable to load model"); - return; - } - } rec = vosk_recognizer_new(mdl,sampleRate); if(rec == nullptr) { - throwJS("Unable to initialize recognizer"); + fireEv("_continue", "Unable to initialize recognizer", this->index); return; } while(!done.test()) { - emscripten_console_log("In loop"); - controller.wait(false, std::memory_order_relaxed); + fireEv("_continue", "." ,this->index); + controller1.wait(false, std::memory_order_relaxed); + controller2.wait(false, std::memory_order_relaxed); if(!done.test()) { switch(vosk_recognizer_accept_waveform_f(rec, dataPtr, 512)) { case 0: - fireEv("result", vosk_recognizer_result(rec)); + fireEv("result", vosk_recognizer_result(rec), this->index); break; case 1: - fireEv("partialResult", vosk_recognizer_partial_result(rec)); + fireEv("partialResult", vosk_recognizer_partial_result(rec), this->index); } } } @@ -33,25 +25,22 @@ recognizer::recognizer(model* mdl, float sampleRate, int index) : index(index) { } recognizer::~recognizer() { done.test_and_set(std::memory_order_relaxed); - controller.notify_one(); + controller1.notify_one(); vosk_recognizer_free(rec); free(dataPtr); } -void recognizer::fireEv(const char *type, const char *content) { - static pthread_t targetThrd{pthread_self()}; - static ProxyingQueue pq{}; - pq.proxyAsync(targetThrd, [&](){ - EM_ASM({ - let ev = new CustomEvent(UTF8ToString($1), {"details" : UTF8ToString($2)}); - objs[$0].dispatchEvent(ev); - console.log(objs[$0], ev) - },index, type, content); - }); -} + void recognizer::acceptWaveForm() { - controller.notify_one(); + controller1.test_and_set(std::memory_order_relaxed); + controller1.notify_one(); + controller1.clear(std::memory_order_relaxed); + controller1.notify_one(); //Make sure c1 is locked before unlocking c2 + controller2.test_and_set(std::memory_order_relaxed); + controller2.notify_one(); + controller2.clear(std::memory_order_relaxed); + controller2.notify_one(); emscripten_console_log("Unblocked"); - fireEv("result", "Test event"); + fireEv("result", "Test event", index); } void recognizer::setGrm(const std::string& grm) { vosk_recognizer_set_grm(rec, grm.c_str()); diff --git a/src/recognizer.h b/src/recognizer.h index a966868..060d1b0 100644 --- a/src/recognizer.h +++ b/src/recognizer.h @@ -13,14 +13,14 @@ namespace fs = std::filesystem; struct recognizer { std::atomic_flag done{}; - std::atomic_flag controller{}; + std::atomic_flag controller1{}; + std::atomic_flag controller2{}; float* dataPtr{}; int index{}; VoskRecognizer* rec{}; recognizer(model* model, float sampleRate, int index); ~recognizer(); void acceptWaveForm(); - void fireEv(const char* type, const char* content); void setSpkModel(spkModel* model); void setGrm(const std::string& grm); void setWords(bool words); diff --git a/src/spkModel.cc b/src/spkModel.cc index 58aa4f0..82e786d 100644 --- a/src/spkModel.cc +++ b/src/spkModel.cc @@ -1,5 +1,5 @@ #include "spkModel.h" -spkModel::spkModel(const std::string& storepath, const std::string& id) : genericModel(storepath, id) { +spkModel::spkModel(const std::string& storepath, const std::string& id, int index) : genericModel(storepath, id, index) { mdl = vosk_spk_model_new("."); if(mdl == nullptr) { throwJS("Unable to initialize speaker model"); @@ -8,11 +8,16 @@ spkModel::spkModel(const std::string& storepath, const std::string& id) : generi spkModel::~spkModel() { vosk_spk_model_free(mdl); } -bool spkModel::checkModelId() { - return genericModel::checkModelId(); +void spkModel::checkModel() { + genericModel::checkModel(); } -bool spkModel::afterFetch(int addr, size_t size) { - return genericModel::afterFetch(addr,size); +void spkModel::afterFetch(int addr, size_t size) { + genericModel::afterFetch(addr,size); +} +bool spkModel::load() { + mdl = vosk_spk_model_new(storepath.c_str()); + if(mdl == nullptr) return false; + return true; } bool spkModel::checkModelFiles() { return fs::exists("mfcc.conf") && diff --git a/src/spkModel.h b/src/spkModel.h index 880cfd0..8bb5d42 100644 --- a/src/spkModel.h +++ b/src/spkModel.h @@ -4,11 +4,9 @@ struct spkModel : genericModel { bool checkModelFiles(); VoskSpkModel* mdl{}; - spkModel(const std::string& storepath, const std::string& id); - bool checkModelId(); - bool afterFetch(int addr, size_t size); + spkModel(const std::string& storepath, const std::string& id, int index); + void checkModel(); + void afterFetch(int addr, size_t size); + bool load(); ~spkModel(); }; - - -