Enforce one nameing convention
This commit is contained in:
6
API.md
6
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``` |
|
||||
|
||||
53
AddCOI.js
Normal file
53
AddCOI.js
Normal 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();
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
55
addCOI.js
55
addCOI.js
@@ -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
34
src/Bindings.cc
Normal 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
39
src/CommonModel.cc
Normal 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
21
src/CommonModel.h
Normal 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();
|
||||
};
|
||||
|
||||
@@ -1,57 +1,60 @@
|
||||
#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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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<VoskSpkModel*>(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);
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "genericModel.h"
|
||||
#include "CommonModel.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
@@ -10,22 +10,23 @@ struct audioData {
|
||||
int len;
|
||||
audioData(int start, int len) : data{reinterpret_cast<float*>(start)}, len{len} {}
|
||||
};
|
||||
struct recognizer {
|
||||
struct Recognizer {
|
||||
std::atomic_bool done{};
|
||||
std::atomic_bool blocker{};
|
||||
int index;
|
||||
std::thread t;
|
||||
VoskRecognizer* rec;
|
||||
std::queue<audioData> 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<audioData> 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);
|
||||
@@ -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<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);
|
||||
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;
|
||||
}
|
||||
@@ -10,6 +10,13 @@
|
||||
#include <emscripten/console.h>
|
||||
|
||||
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);
|
||||
@@ -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
|
||||
@@ -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());
|
||||
};
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
12
src/make
12
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user