Design structure change again........ OMG STOPPP

This commit is contained in:
msqr1
2024-01-29 22:44:09 -08:00
parent c34d3f8974
commit c20efbeb29
12 changed files with 153 additions and 127 deletions

View File

@@ -5,16 +5,14 @@
using namespace emscripten; using namespace emscripten;
EMSCRIPTEN_BINDINGS() { EMSCRIPTEN_BINDINGS() {
function("setLogLevel", &vosk_set_log_level, allow_raw_pointers()); function("setLogLevel", &vosk_set_log_level, allow_raw_pointers());
class_<model>("Model") class_<model>("model")
.constructor<std::string, std::string>(allow_raw_pointers()) .constructor<std::string, std::string, int>(allow_raw_pointers())
.function("checkModelFiles", &model::checkModelFiles, allow_raw_pointers()) .function("checkModel", &spkModel::checkModel, allow_raw_pointers())
.function("checkModelId", &model::checkModelId, allow_raw_pointers()) .function("afterFetch", &spkModel::afterFetch, allow_raw_pointers());
.function("afterFetch", &model::afterFetch, allow_raw_pointers());
class_<spkModel>("SpkModel") class_<spkModel>("spkModel")
.constructor<std::string, std::string>(allow_raw_pointers()) .constructor<std::string, std::string, int>(allow_raw_pointers())
.function("checkModelFiles", &spkModel::checkModelFiles, allow_raw_pointers()) .function("checkModel", &spkModel::checkModel, allow_raw_pointers())
.function("checkModelId", &spkModel::checkModelId, allow_raw_pointers())
.function("afterFetch", &spkModel::afterFetch, allow_raw_pointers()); .function("afterFetch", &spkModel::afterFetch, allow_raw_pointers());
class_<recognizer>("recognizer") class_<recognizer>("recognizer")

View File

@@ -1,10 +1,11 @@
#include "genericModel.h" #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::current_path("/opfs");
fs::create_directories(storepath); fs::create_directories(storepath);
fs::current_path(storepath); fs::current_path(storepath);
} }
bool genericModel::checkModelId() { bool genericModel::checkModel() {
if(!checkModelFiles()) return false;
static std::error_code c{}; static std::error_code c{};
if(!fs::exists("id", c)) return false; if(!fs::exists("id", c)) return false;
std::ifstream file {"id", std::ifstream::binary}; std::ifstream file {"id", std::ifstream::binary};
@@ -15,18 +16,28 @@ bool genericModel::checkModelId() {
file.read(&oldid[0], size); file.read(&oldid[0], size);
return id.compare(oldid) == 0 ? true : false; return id.compare(oldid) == 0 ? true : false;
} }
bool genericModel::afterFetch(int memAddr, size_t size) { void genericModel::afterFetch(int memAddr, size_t size) {
if(!extractModel(reinterpret_cast<char*>(memAddr), size)) { // FIXME: Recognizer can reuse this thread to avoid respawning threads
return false; std::thread t{[this, memAddr, size](){
} char* modelData = reinterpret_cast<char*>(memAddr);
std::ofstream idFile("id"); if(!extractModel(modelData, size)) {
if(!idFile.is_open()) { free(modelData);
fs::current_path("/opfs"); fireEv("_continue", "Unable to extract model", index);
fs::remove_all(storepath); return;
return false; }
} free(modelData);
idFile << id; std::ofstream idFile("id");
return true; 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) { bool genericModel::extractModel(const char* fileBuf, size_t size) {
std::string path{}; std::string path{};

View File

@@ -14,10 +14,12 @@ namespace fs = std::filesystem;
struct genericModel { struct genericModel {
const std::string storepath{}; const std::string storepath{};
const std::string id{}; const std::string id{};
int index{};
static bool extractModel(const char* fileBuf, size_t size); static bool extractModel(const char* fileBuf, size_t size);
virtual bool checkModelFiles() = 0; virtual bool checkModelFiles() = 0;
bool checkModelId(); virtual bool load() = 0;
bool afterFetch(int memAddr, size_t size); bool checkModel();
genericModel(const std::string &storepath, const std::string &id); void afterFetch(int memAddr, size_t size);
genericModel(const std::string &storepath, const std::string &id, int index);
}; };

View File

@@ -1,27 +1,26 @@
#include "global.h" #include "global.h"
// Throw error for user, or just throw the message for internal communication
void throwJS(const char* msg, bool err) { 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{}; static ProxyingQueue pq{};
if(pthread_self() == targetThrd) { static pthread_t selfTID {pthread_self()};
pq.proxySync(selfTID, [&](){
EM_ASM({ EM_ASM({
if($1) { let ev = new CustomEvent(UTF8ToString($1), {"details" : UTF8ToString($2)});
throw Error(UTF8ToString($0)); objs[$0].dispatchEvent(ev);
return; console.log(objs[$0], ev)
} },index, type, content);
throw UTF8ToString($0);
},msg, err);
}
pq.proxyAsync(targetThrd, [&](){
EM_ASM({
if($1) {
throw Error(UTF8ToString($0));
return;
}
throw UTF8ToString($0);
},msg, err);
}); });
} }
int main() { int main() {
//vosk_set_log_level(-1); //vosk_set_log_level(-1);
std::thread t{[](){ std::thread t{[](){

View File

@@ -6,7 +6,8 @@
#include <emscripten/console.h> #include <emscripten/console.h>
#include <emscripten/em_asm.h> #include <emscripten/em_asm.h>
#include <emscripten/proxying.h> #include <emscripten/proxying.h>
using namespace emscripten;
using namespace emscripten;
void throwJS(const char* msg, bool err = false); void throwJS(const char* msg, bool err = false);
void fireEv(const char *type, const char *content, int index);
int main(); int main();

View File

@@ -1,14 +1,19 @@
#include "model.h" #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() { model::~model() {
vosk_model_free(mdl); vosk_model_free(mdl);
} }
bool model::checkModelId() { void model::afterFetch(int addr, size_t size) {
return genericModel::checkModelId(); genericModel::afterFetch(addr,size);
} }
bool model::afterFetch(int addr, size_t size) { void model::checkModel() {
return genericModel::afterFetch(addr,size); genericModel::checkModel();
}
bool model::load() {
mdl = vosk_model_new(storepath.c_str());
if(mdl == nullptr) return false;
return true;
} }
bool model::checkModelFiles() { bool model::checkModelFiles() {
static std::error_code c{}; static std::error_code c{};

View File

@@ -4,9 +4,10 @@
struct model : genericModel { struct model : genericModel {
bool checkModelFiles(); bool checkModelFiles();
VoskModel* mdl{}; VoskModel* mdl{};
model(const std::string& storepath, const std::string& id); model(const std::string& storepath, const std::string& id, int index);
bool checkModelId(); void checkModel();
bool afterFetch(int addr, size_t size); void afterFetch(int addr, size_t size);
bool load();
~model(); ~model();
}; };

View File

@@ -1,8 +1,10 @@
let objs = [] let objs = []
class Recognizer extends EventTarget { class Recognizer extends EventTarget {
constructor(rec) { constructor() {
super() super()
this.obj = rec }
_init(model, sampleRate) {
this.obj = new Module.recognizer(model, sampleRate, objs.length)
objs.push(this) objs.push(this)
this.ptr = Module._malloc(512) this.ptr = Module._malloc(512)
} }
@@ -47,48 +49,64 @@ class Recognizer extends EventTarget {
this.obj.setMaxAlternatives(alts) 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 = () => { Module.deleteAll = () => {
objs.forEach(obj => obj.delete()) objs.forEach(obj => obj.delete())
} }
Module.makeModel = async (url, storepath, id) => { Module.makeModel = async (url, storepath, id) => {
let mdl = new Module.Model(storepath, id) let mdl = new Model()
let mdlMem; mdl.obj(new Module.model(url, storepath, objs.length))
if(mdl.checkModelFiles() && mdl.checkModelId()) { objs.push(mdl)
objs.push(mdl) let retval = new Promise((resolve, reject) => {
return mdl rec.addEventListener("_continue", (ev) => {
} if(mdl.checkModel()) {
}
if(ev.details === ".") {
resolve(mdl)
}
reject(ev.details)
}, {once : true})
})
let mdlMem
try { try {
let res = await fetch(url) let res = await fetch(url)
if(!res.ok) throw res.statusText if(!res.ok) throw res.statusText
let arr = await res.arrayBuffer() 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) Module.HEAP8.set(new Int8Array(arr), mdlMem)
if(!mdl.afterFetch(mdlMem, arr.byteLength)) throw "Unable to extract model and write ID" mdl.afterFetch(mdlMem, arr.byteLength)
if(!mdl.checkModelFiles()) throw "Model contains invalid model files"
} }
catch(e) { catch(e) {
mdl.delete() mdl.delete()
return Promise.reject(e.message || e) return Promise.reject(e.message || e)
} }
finally {
Module._free(mdlMem)
}
objs.push(mdl) objs.push(mdl)
return mdl return mdl
} }
Module.makeSpkModel = async (url, storepath, id) => { Module.makeSpkModel = async (url, storepath, id) => {
let mdl = new Module.SpkModel(storepath, id) let mdl = new Module.SpkModel(storepath, id)
let mdlMem; let mdlMem
if(mdl.checkModelFiles() && mdl.checkModelId()) {
objs.push(mdl)
return mdl
}
try { try {
if(mdl.checkModelFiles() && mdl.checkModelId()) {
objs.push(mdl)
return mdl
}
let res = await fetch(url) let res = await fetch(url)
if(!res.ok) throw res.statusText if(!res.ok) throw res.statusText
let arr = await res.arrayBuffer() let arr = await res.arrayBuffer()
mdlMem = Module._malloc(arr.byteLength) 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.afterFetch(mdlMem, arr.byteLength)) throw "Unable to extract model and write ID"
if(!mdl.checkModelFiles()) throw "Model contains invalid model files" if(!mdl.checkModelFiles()) throw "Model contains invalid model files"
} }
@@ -96,20 +114,19 @@ Module.makeSpkModel = async (url, storepath, id) => {
mdl.delete() mdl.delete()
return Promise.reject(e.message || e) return Promise.reject(e.message || e)
} }
finally {
Module._free(mdlMem)
}
objs.push(mdl) objs.push(mdl)
return mdl return mdl
} }
Module.makeRecognizer = async (model, sampleRate) => { Module.makeRecognizer = (model, sampleRate) => {
let rec let rec = new Recognizer()
try { let retval = new Promise((resolve, reject) => {
rec = new Module.recognizer(model, sampleRate, objs.length) rec.addEventListener("_continue", (ev) => {
} if(ev.details == ".") {
catch(e) { resolve(rec)
rec.delete() }
return Promise.reject(e) reject(ev.details)
} }, {once : true})
return new Recognizer(rec) })
rec._init(model.obj, sampleRate)
return retval
} }

View File

@@ -1,30 +1,22 @@
#include "recognizer.h" #include "recognizer.h"
recognizer::recognizer(model* mdl, float sampleRate, int index) : index(index) { 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){ 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); rec = vosk_recognizer_new(mdl,sampleRate);
if(rec == nullptr) { if(rec == nullptr) {
throwJS("Unable to initialize recognizer"); fireEv("_continue", "Unable to initialize recognizer", this->index);
return; return;
} }
while(!done.test()) { while(!done.test()) {
emscripten_console_log("In loop"); fireEv("_continue", "." ,this->index);
controller.wait(false, std::memory_order_relaxed); controller1.wait(false, std::memory_order_relaxed);
controller2.wait(false, std::memory_order_relaxed);
if(!done.test()) { if(!done.test()) {
switch(vosk_recognizer_accept_waveform_f(rec, dataPtr, 512)) { switch(vosk_recognizer_accept_waveform_f(rec, dataPtr, 512)) {
case 0: case 0:
fireEv("result", vosk_recognizer_result(rec)); fireEv("result", vosk_recognizer_result(rec), this->index);
break; break;
case 1: 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() { recognizer::~recognizer() {
done.test_and_set(std::memory_order_relaxed); done.test_and_set(std::memory_order_relaxed);
controller.notify_one(); controller1.notify_one();
vosk_recognizer_free(rec); vosk_recognizer_free(rec);
free(dataPtr); 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() { 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"); emscripten_console_log("Unblocked");
fireEv("result", "Test event"); fireEv("result", "Test event", index);
} }
void recognizer::setGrm(const std::string& grm) { void recognizer::setGrm(const std::string& grm) {
vosk_recognizer_set_grm(rec, grm.c_str()); vosk_recognizer_set_grm(rec, grm.c_str());

View File

@@ -13,14 +13,14 @@ namespace fs = std::filesystem;
struct recognizer { struct recognizer {
std::atomic_flag done{}; std::atomic_flag done{};
std::atomic_flag controller{}; std::atomic_flag controller1{};
std::atomic_flag controller2{};
float* dataPtr{}; float* dataPtr{};
int index{}; int index{};
VoskRecognizer* rec{}; VoskRecognizer* rec{};
recognizer(model* model, float sampleRate, int index); recognizer(model* model, float sampleRate, int index);
~recognizer(); ~recognizer();
void acceptWaveForm(); void acceptWaveForm();
void fireEv(const char* type, const char* content);
void setSpkModel(spkModel* model); void setSpkModel(spkModel* model);
void setGrm(const std::string& grm); void setGrm(const std::string& grm);
void setWords(bool words); void setWords(bool words);

View File

@@ -1,5 +1,5 @@
#include "spkModel.h" #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("."); mdl = vosk_spk_model_new(".");
if(mdl == nullptr) { if(mdl == nullptr) {
throwJS("Unable to initialize speaker model"); throwJS("Unable to initialize speaker model");
@@ -8,11 +8,16 @@ spkModel::spkModel(const std::string& storepath, const std::string& id) : generi
spkModel::~spkModel() { spkModel::~spkModel() {
vosk_spk_model_free(mdl); vosk_spk_model_free(mdl);
} }
bool spkModel::checkModelId() { void spkModel::checkModel() {
return genericModel::checkModelId(); genericModel::checkModel();
} }
bool spkModel::afterFetch(int addr, size_t size) { void spkModel::afterFetch(int addr, size_t size) {
return genericModel::afterFetch(addr,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() { bool spkModel::checkModelFiles() {
return fs::exists("mfcc.conf") && return fs::exists("mfcc.conf") &&

View File

@@ -4,11 +4,9 @@
struct spkModel : genericModel { struct spkModel : genericModel {
bool checkModelFiles(); bool checkModelFiles();
VoskSpkModel* mdl{}; VoskSpkModel* mdl{};
spkModel(const std::string& storepath, const std::string& id); spkModel(const std::string& storepath, const std::string& id, int index);
bool checkModelId(); void checkModel();
bool afterFetch(int addr, size_t size); void afterFetch(int addr, size_t size);
bool load();
~spkModel(); ~spkModel();
}; };