Enforce one nameing convention

This commit is contained in:
msqr1
2024-08-19 18:53:52 -07:00
parent 01105868c6
commit 2122902190
24 changed files with 211 additions and 211 deletions

6
API.md
View File

@@ -47,7 +47,7 @@
SharedArrayBuffer is necessary to share data between threads, so these response headers must be set: SharedArrayBuffer is necessary to share data between threads, so these response headers must be set:
- ```Cross-Origin-Embedder-Policy``` ⟶ ```require-corp``` - ```Cross-Origin-Embedder-Policy``` ⟶ ```require-corp```
- ```Cross-Origin-Opener-Policy``` ⟶ ```same-origin``` - ```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 ## CSP headers
Pthread worker construction must be from a blob (see [Emscripten issue](https://github.com/emscripten-core/emscripten/issues/21937)), so the CSP: 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. 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 # 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``` ```sudo apt install autotools-dev autoconf libtool make pkg-config```
- Changing any option to non-default values requires recompilation - Changing any option to non-default values requires recompilation
@@ -70,7 +70,7 @@ cd Vosklet/src &&
``` ```
| Option | Description | Default value | | 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) | | 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)``` | | JOBS | Set the number of jobs (threads) when building | ```$(nproc)``` |
| EMSDK | Set EMSDK's path (will install EMSDK in root folder if unset) | ```../emsdk``` | | EMSDK | Set EMSDK's path (will install EMSDK in root folder if unset) | ```../emsdk``` |

53
AddCOI.js Normal file
View File

@@ -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();
}
}
})();
}

View File

@@ -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();
}
}
})();
}

34
src/Bindings.cc Normal file
View File

@@ -0,0 +1,34 @@
#include "CommonModel.h"
#include "Recognizer.h"
#include <emscripten/bind.h>
using namespace emscripten;
EMSCRIPTEN_BINDINGS() {
function("setLogLevel", &vosk_set_log_level, allow_raw_pointers());
enum_<VoskEndpointerMode>("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>("CommonModel")
.constructor<int, bool, std::string, std::string, int, int>(allow_raw_pointers())
.function("findWord", &CommonModel::findWord, allow_raw_pointers());
class_<Recognizer>("Recognizer")
.constructor<int, float, CommonModel*>(allow_raw_pointers())
.constructor<int, float, CommonModel*, CommonModel*>(allow_raw_pointers())
.constructor<int, float, CommonModel*, std::string, int>(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());
};

39
src/CommonModel.cc Normal file
View File

@@ -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<unsigned char*>(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<VoskModel*>(mdl) != nullptr : std::get<VoskSpkModel*>(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<VoskModel*>(mdl), word.c_str());
}
CommonModel::~CommonModel() {
if(normalMdl) vosk_model_free(std::get<VoskModel*>(mdl));
else vosk_spk_model_free(std::get<VoskSpkModel*>(mdl));
if(t.joinable()) t.join();
}

21
src/CommonModel.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "Util.h"
#include <vosk_api.h>
extern void free(void*);
struct CommonModel {
bool normalMdl;
bool thrdUsed{};
int index;
std::thread t;
std::string storepath;
std::string id;
std::variant<VoskModel*, VoskSpkModel*> 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();
};

View File

@@ -1,57 +1,60 @@
#include "recognizer.h" #include "recognizer.h"
recognizer::recognizer(int index, float sampleRate, genericModel* model) : index{index}, rec{vosk_recognizer_new(std::get<VoskModel*>(model->mdl),sampleRate)} { recognizer::recognizer(int index, float sampleRate, CommonModel* model) : index{index}, rec{vosk_recognizer_new(std::get<VoskModel*>(model->mdl),sampleRate)}, t{std::move(model->t)} {
finishConstruction(model); finishConstruction(model);
} }
recognizer::recognizer(int index, float sampleRate, genericModel* model, genericModel* spkModel) : index{index}, rec{vosk_recognizer_new_spk(std::get<VoskModel*>(model->mdl), sampleRate, std::get<VoskSpkModel*>(spkModel->mdl))} { recognizer::recognizer(int index, float sampleRate, CommonModel* model, CommonModel* spkModel) : index{index}, rec{vosk_recognizer_new_spk(std::get<VoskModel*>(model->mdl), sampleRate, std::get<VoskSpkModel*>(spkModel->mdl))}, t{std::move(model->t)} {
finishConstruction(model, spkModel); 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<VoskModel*>(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<VoskModel*>(model->mdl), sampleRate, grm.c_str())}, t{std::move(model->t)} {
finishConstruction(model); finishConstruction(model);
} }
recognizer::~recognizer() { recognizer::~recognizer() {
done = true; done = true;
vosk_recognizer_free(rec); 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) { if(rec == nullptr) {
fireEv(index, "Unable to initialize recognizer"); fireEv(index, "Unable to initialize recognizer");
return; return;
} }
auto main {[this](){ auto main {[this](){
fireEv(index, "0"); fireEv(index, "0");
audioData* next;
while(!done) { while(!done) {
blocker.wait(done, std::memory_order_relaxed); blocker.wait(done, std::memory_order_relaxed);
blocker = false; blocker = false;
next = &dataQ.front();
while(!dataQ.empty()) { 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: case 0:
fireEv(index, vosk_recognizer_result(rec), "result"); fireEv(index, vosk_recognizer_result(rec), "result");
break; break;
case 1: case 1:
fireEv(index, vosk_recognizer_partial_result(rec), "partialResult"); fireEv(index, vosk_recognizer_partial_result(rec), "partialResult");
} }
free(dataQ.front().data); free(next->data);
dataQ.pop(); dataQ.pop();
} }
} }
}}; }};
if(!model->resourceUsed) { if(!model->thrdUsed) {
model->resourceUsed = true; model->thrdUsed = true;
model->func = main; model->func = main;
model->blocker = true; model->blocker = true;
model->blocker.notify_one(); model->blocker.notify_one();
return; return;
} }
if(spkModel != nullptr && !spkModel->resourceUsed) { if(spkModel != nullptr && !spkModel->thrdUsed) {
spkModel->resourceUsed = true; spkModel->thrdUsed = true;
spkModel->func = main; spkModel->func = main;
spkModel->blocker = true; spkModel->blocker = true;
model->blocker.notify_one(); model->blocker.notify_one();
return; return;
} }
std::thread t{main}; blocker.is_always_lock_free
t.detach(); std::thread{main}.detach();
} }
void recognizer::pushData(int start, int len) { void recognizer::pushData(int start, int len) {
dataQ.emplace(start, 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) { void recognizer::setGrm(const std::string& grm) {
vosk_recognizer_set_grm(rec, grm.c_str()); 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<VoskSpkModel*>(spkModel->mdl)); vosk_recognizer_set_spk_model(rec, std::get<VoskSpkModel*>(spkModel->mdl));
} }
void recognizer::setWords(bool words) { void recognizer::setWords(bool words) {
vosk_recognizer_set_words(rec,words); vosk_recognizer_set_words(rec, words);
} }
void recognizer::setPartialWords(bool partialWords) { void recognizer::setPartialWords(bool partialWords) {
vosk_recognizer_set_partial_words(rec, partialWords); vosk_recognizer_set_partial_words(rec, partialWords);

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#include "genericModel.h" #include "CommonModel.h"
#include <queue> #include <queue>
@@ -10,22 +10,23 @@ struct audioData {
int len; int len;
audioData(int start, int len) : data{reinterpret_cast<float*>(start)}, len{len} {} audioData(int start, int len) : data{reinterpret_cast<float*>(start)}, len{len} {}
}; };
struct recognizer { struct Recognizer {
std::atomic_bool done{}; std::atomic_bool done{};
std::atomic_bool blocker{}; std::atomic_bool blocker{};
int index; int index;
std::thread t;
VoskRecognizer* rec; VoskRecognizer* rec;
std::queue<audioData> dataQ{}; std::queue<audioData> dataQ;
recognizer(int index, float sampleRate, genericModel* model); Recognizer(int index, float sampleRate, CommonModel* model);
recognizer(int index, float sampleRate, genericModel* model, genericModel* spkModel); Recognizer(int index, float sampleRate, CommonModel* model, CommonModel* spkModel);
recognizer(int index, float sampleRate, genericModel* model, const std::string& grm, int); Recognizer(int index, float sampleRate, CommonModel* model, const std::string& grm, int);
~recognizer(); ~Recognizer();
void finishConstruction(genericModel* model, genericModel* spkModel = nullptr); void finishConstruction(CommonModel* model, CommonModel* spkModel = nullptr);
void pushData(int start, int len); void pushData(int start, int len);
void reset(); void reset();
void setEndpointerMode(VoskEndpointerMode mode); void setEndpointerMode(VoskEndpointerMode mode);
void setEndpointerDelays(float tStartMax, float tEnd, float tMax); void setEndpointerDelays(float tStartMax, float tEnd, float tMax);
void setSpkModel(genericModel* model); void setSpkModel(CommonModel* model);
void setGrm(const std::string& grm); void setGrm(const std::string& grm);
void setWords(bool words); void setWords(bool words);
void setPartialWords(bool partialWords); void setPartialWords(bool partialWords);

View File

@@ -1,12 +1,11 @@
#include "util.h" #include "Util.h"
void fireEv(int index, const char* content, const char* type) { void fireEv(int index, const char* content, const char* type) {
MAIN_THREAD_EM_ASM({ MAIN_THREAD_EM_ASM({
objs[$0].dispatchEvent(new CustomEvent($2 === 0 ? "0" : UTF8ToString($2), { "detail" : UTF8ToString($1) })); objs[$0].dispatchEvent(new CustomEvent($2 === 0 ? "0" : UTF8ToString($2), { "detail" : UTF8ToString($1) }));
}, index, content, type); }, index, content, type);
} }
int untar(unsigned char* tar, int tarSize, const std::string& storepath) { 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; unsigned char* ptr = tar;
size_t size{0}; size_t size{0};
std::string path{}; std::string path{};
@@ -15,7 +14,7 @@ int untar(unsigned char* tar, int tarSize, const std::string& storepath) {
while(ptr <= end) { while(ptr <= end) {
if(ptr[156] != '5' && ptr[156] != 0 && if(ptr[156] != '5' && ptr[156] != 0 &&
ptr[156] != '0') { ptr[156] != '0') {
return 2; return IncorrectFiletype;
} }
path.clear(); path.clear();
path += reinterpret_cast<char*>(ptr + 345); path += reinterpret_cast<char*>(ptr + 345);
@@ -36,12 +35,12 @@ int untar(unsigned char* tar, int tarSize, const std::string& storepath) {
if(size == 0) fs::create_directory(path); if(size == 0) fs::create_directory(path);
else { else {
int fd {open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0777)}; 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); int res = write(fd, ptr, size);
if(res == -1) return 4; if(res == -1) return FailedWrite;
if(close(fd) == -1) return 5; if(close(fd) == -1) return FailedClose;
ptr += size + 512 - size % 512; ptr += size + 512 - size % 512;
} }
} }
return 0; return Successful;
} }

View File

@@ -10,6 +10,13 @@
#include <emscripten/console.h> #include <emscripten/console.h>
namespace fs = std::filesystem; namespace fs = std::filesystem;
enum UntarStatus {
Successful,
IncorrectFormat,
IncorrectFiletype,
FailedOpen,
FailedWrite,
FailedClose
};
void fireEv(int index, const char* content, const char* type = nullptr); void fireEv(int index, const char* content, const char* type = nullptr);
int untar(unsigned char* tar, int tarSize, const std::string& storepath); int untar(unsigned char* tar, int tarSize, const std::string& storepath);

View File

@@ -44,25 +44,23 @@ async function getFileHandle(path, create = false) {
return prevDir.getFileHandle(components[components.length - 1], { create : create }) return prevDir.getFileHandle(components[components.length - 1], { create : create })
} }
class genericModel extends EventTarget { class CommonModel extends EventTarget {
constructor() { constructor() {
super() super()
objs.push(this) objs.push(this)
} }
static async create(url, storepath, id, normalMdl) { static async create(url, storepath, id, normalMdl) {
let mdl = new genericModel() let mdl = new CommonModel()
let result = new Promise((resolve, reject) => { let result = new Promise((resolve, reject) => {
mdl.addEventListener("0", ev => { mdl.addEventListener("0", ev => {
if(ev.detail == "0") { if(ev.detail == "0") {
if(normalMdl) mdl.findWord = (word) => mdl.obj.findWord(word) if(normalMdl) mdl.findWord = (word) => mdl.obj.findWord(word)
return resolve(mdl) return resolve(mdl)
} }
mdl.delete()
reject(ev.detail) reject(ev.detail)
}, { once : true }) }, { once : true })
}) })
let tar let tar
mdl.obj = new Module.genericModel(objs.length - 1, normalMdl, "/" + storepath, id)
try { try {
let dataFile = await (await getFileHandle(storepath + "/model.tgz")).getFile() let dataFile = await (await getFileHandle(storepath + "/model.tgz")).getFile()
let idFile = await (await getFileHandle(storepath + "/id")).getFile() let idFile = await (await getFileHandle(storepath + "/id")).getFile()
@@ -89,7 +87,7 @@ class genericModel extends EventTarget {
} }
let tarStart = Module._malloc(tar.byteLength) let tarStart = Module._malloc(tar.byteLength)
Module.HEAPU8.set(new Uint8Array(tar), tarStart) 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 return result
} }
delete() { delete() {
@@ -98,14 +96,14 @@ class genericModel extends EventTarget {
} }
Module.createModel = async (url, storepath, id) => { 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) => { 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() { constructor() {
super() super()
objs.push(this) objs.push(this)
@@ -116,7 +114,7 @@ class recognizer extends EventTarget {
}) })
} }
static async create(model, sampleRate, mode, grammar, spkModel) { static async create(model, sampleRate, mode, grammar, spkModel) {
let rec = new recognizer() let rec = new Recognizer()
let result = new Promise((resolve, reject) => { let result = new Promise((resolve, reject) => {
rec.addEventListener("0", ev => { rec.addEventListener("0", ev => {
if(ev.detail == "0") return resolve(rec) if(ev.detail == "0") return resolve(rec)
@@ -126,13 +124,13 @@ class recognizer extends EventTarget {
}) })
switch(mode) { switch(mode) {
case 1: case 1:
rec.obj = new Module.recognizer(objs.length - 1, sampleRate, model) rec.obj = new Module.Recognizer(objs.length - 1, sampleRate, model)
break break
case 2: 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 break
default: 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 return result
} }
@@ -147,15 +145,15 @@ class recognizer extends EventTarget {
} }
Module.createRecognizer = (model, sampleRate) => { Module.createRecognizer = (model, sampleRate) => {
return recognizer.create(model.obj, sampleRate, 1) return Recognizer.create(model.obj, sampleRate, 1)
} }
Module.createRecognizerWithSpkModel = (model, sampleRate, spkModel) => { 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) => { 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 // See Emscripten issue #21937

View File

@@ -1,35 +0,0 @@
#include "genericModel.h"
#include "recognizer.h"
#include <emscripten/bind.h>
using namespace emscripten;
EMSCRIPTEN_BINDINGS() {
function("setLogLevel", &vosk_set_log_level, allow_raw_pointers());
enum_<VoskEndpointerMode>("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>("genericModel")
.constructor<int, bool, std::string, std::string>(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())
.constructor<int, float, genericModel*, genericModel*>(allow_raw_pointers())
.constructor<int, float, genericModel*, std::string, int>(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());
};

View File

@@ -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<unsigned char*>(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<VoskModel*>(mdl) != nullptr : std::get<VoskSpkModel*>(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<VoskModel*>(mdl), word.c_str());
}
genericModel::~genericModel() {
if(normalMdl) vosk_model_free(std::get<VoskModel*>(mdl));
else vosk_spk_model_free(std::get<VoskSpkModel*>(mdl));
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include "util.h"
#include <vosk_api.h>
extern void free(void*);
struct genericModel {
bool normalMdl;
bool resourceUsed{};
std::atomic_bool blocker{};
int index;
std::string storepath;
std::string id;
std::variant<VoskModel*, VoskSpkModel*> mdl;
std::function<void()> func;
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

@@ -41,7 +41,7 @@ if [ ! -d $OPENFST ]; then
rm -rf /tmp/openfst && rm -rf /tmp/openfst &&
git clone --depth=1 https://github.com/alphacep/openfst /tmp/openfst && git clone --depth=1 https://github.com/alphacep/openfst /tmp/openfst &&
cd /tmp/openfst && cd /tmp/openfst &&
git apply $SRC/openfst.patch git apply $SRC/Openfst.patch
autoreconf -is && 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 && 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 && emmake make -j$JOBS install &&
@@ -51,14 +51,14 @@ fi
if [ ! -d $CLAPACK_WASM ]; then if [ ! -d $CLAPACK_WASM ]; then
git clone --depth=1 https://gitlab.inria.fr/multispeech/kaldi.web/clapack-wasm.git $CLAPACK_WASM && git clone --depth=1 https://gitlab.inria.fr/multispeech/kaldi.web/clapack-wasm.git $CLAPACK_WASM &&
cd $CLAPACK_WASM && cd $CLAPACK_WASM &&
git apply $SRC/clapack-wasm.patch && git apply $SRC/Clapack-wasm.patch &&
bash install_repo.sh emcc bash install_repo.sh emcc
fi fi
if [ ! -d $KALDI ]; then if [ ! -d $KALDI ]; then
git clone -b vosk --depth=1 https://github.com/alphacep/kaldi $KALDI && git clone -b vosk --depth=1 https://github.com/alphacep/kaldi $KALDI &&
cd $KALDI/src && 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 && 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 emmake make -j$JOBS online2 rnnlm
fi fi
@@ -66,14 +66,14 @@ fi
if [ ! -d $VOSK ]; then if [ ! -d $VOSK ]; then
git clone -b v0.3.50 --depth=1 https://github.com/alphacep/vosk-api $VOSK && git clone -b v0.3.50 --depth=1 https://github.com/alphacep/vosk-api $VOSK &&
cd $VOSK/src && cd $VOSK/src &&
git apply $SRC/vosk.patch && git apply $SRC/Vosk.patch &&
VOSK_FILES="recognizer.cc language_model.cc model.cc spk_model.cc vosk_api.cc" && 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 && 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} emar -rcs vosk.a ${VOSK_FILES//.cc/.o}
fi fi
cd $SRC && 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 .. && cd .. &&
rm -f Vosklet.worker.js rm -f Vosklet.worker.js