Revamp event system to not switch from C++ --> JS twice. Fix transferer node offset

This commit is contained in:
msqr1
2024-10-02 20:42:39 -07:00
parent da3c7a269a
commit 30228eb0c1
9 changed files with 49 additions and 47 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -5,7 +5,7 @@
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);
enum_<VoskEndpointerMode>("EpMode") enum_<VoskEndpointerMode>("EpMode")
.value("ANSWER_DEFAULT", VOSK_EP_ANSWER_DEFAULT) .value("ANSWER_DEFAULT", VOSK_EP_ANSWER_DEFAULT)
@@ -14,22 +14,22 @@ EMSCRIPTEN_BINDINGS() {
.value("ANSWER_VERY_LONG", VOSK_EP_ANSWER_VERY_LONG); .value("ANSWER_VERY_LONG", VOSK_EP_ANSWER_VERY_LONG);
class_<CommonModel>("CommonModel") class_<CommonModel>("CommonModel")
.constructor<int, bool, std::string, std::string, int, int>(allow_raw_pointers()) .constructor<int, bool, std::string, std::string, int, int>(return_value_policy::take_ownership())
.function("findWord", &CommonModel::findWord, allow_raw_pointers()); .function("findWord", &CommonModel::findWord);
class_<Recognizer>("Recognizer") class_<Recognizer>("Recognizer")
.constructor<int, float, CommonModel*>(allow_raw_pointers()) .constructor<int, float, CommonModel*>(return_value_policy::take_ownership())
.constructor<int, float, CommonModel*, CommonModel*>(allow_raw_pointers()) .constructor<int, float, CommonModel*, CommonModel*>(return_value_policy::take_ownership())
.constructor<int, float, CommonModel*, std::string, int>(allow_raw_pointers()) .constructor<int, float, CommonModel*, std::string, int>(return_value_policy::take_ownership())
.function("safeDelete", &Recognizer::safeDelete, allow_raw_pointers()) .function("safeDelete", &Recognizer::safeDelete)
.function("acceptWaveform", &Recognizer::acceptWaveform, allow_raw_pointers()) .function("acceptWaveform", &Recognizer::acceptWaveform)
.function("reset", &Recognizer::reset, allow_raw_pointers()) .function("reset", &Recognizer::reset)
.function("setEndpointerMode", &Recognizer::setEndpointerMode, allow_raw_pointers()) .function("setEndpointerMode", &Recognizer::setEndpointerMode)
.function("setEndpointerDelays", &Recognizer::setEndpointerDelays, allow_raw_pointers()) .function("setEndpointerDelays", &Recognizer::setEndpointerDelays)
.function("setWords", &Recognizer::setWords, allow_raw_pointers()) .function("setWords", &Recognizer::setWords)
.function("setPartialWords", &Recognizer::setPartialWords, allow_raw_pointers()) .function("setPartialWords", &Recognizer::setPartialWords)
.function("setGrm", &Recognizer::setGrm, allow_raw_pointers()) .function("setGrm", &Recognizer::setGrm)
.function("setNLSML", &Recognizer::setNLSML, allow_raw_pointers()) .function("setNLSML", &Recognizer::setNLSML)
.function("setSpkModel", &Recognizer::setSpkModel, allow_raw_pointers()) .function("setSpkModel", &Recognizer::setSpkModel, allow_raw_pointers())
.function("setMaxAlternatives", &Recognizer::setMaxAlternatives, allow_raw_pointers()); .function("setMaxAlternatives", &Recognizer::setMaxAlternatives);
}; };

View File

@@ -1,20 +1,20 @@
#include "Recognizer.h" #include "Recognizer.h"
#include <atomic> #include <atomic>
static const char* recognizerInitErr{"Unable to initialize recognizer"}; const char* recognizerInitErr{"Unable to initialize recognizer"};
Recognizer::Recognizer(int index, float sampleRate, CommonModel* model) : Recognizer::Recognizer(int index, float sampleRate, CommonModel* model) :
rec{vosk_recognizer_new(std::get<VoskModel*>(model->mdl), sampleRate)} { rec{vosk_recognizer_new(std::get<VoskModel*>(model->mdl), sampleRate)} {
if(rec == nullptr) mtFireEv(index, Event::status, reinterpret_cast<int>(recognizerInitErr)); if(rec == nullptr) fireEv(index, Event::status, recognizerInitErr);
else globalPool.exec([this, index]{main(index);}); else globalPool.exec([this, index]{main(index);});
} }
Recognizer::Recognizer(int index, float sampleRate, CommonModel* model, CommonModel* spkModel) : Recognizer::Recognizer(int index, float sampleRate, CommonModel* model, CommonModel* spkModel) :
rec{vosk_recognizer_new_spk(std::get<VoskModel*>(model->mdl), sampleRate, std::get<VoskSpkModel*>(spkModel->mdl))} { rec{vosk_recognizer_new_spk(std::get<VoskModel*>(model->mdl), sampleRate, std::get<VoskSpkModel*>(spkModel->mdl))} {
if(rec == nullptr) mtFireEv(index, Event::status, reinterpret_cast<int>(recognizerInitErr)); if(rec == nullptr) fireEv(index, Event::status, recognizerInitErr);
else globalPool.exec([this, index]{main(index);}); else globalPool.exec([this, index]{main(index);});
} }
Recognizer::Recognizer(int index, float sampleRate, CommonModel* model, const std::string& grm, int) : Recognizer::Recognizer(int index, float sampleRate, CommonModel* model, const std::string& grm, int) :
rec{vosk_recognizer_new_grm(std::get<VoskModel*>(model->mdl), sampleRate, grm.c_str())} { rec{vosk_recognizer_new_grm(std::get<VoskModel*>(model->mdl), sampleRate, grm.c_str())} {
if(rec == nullptr) mtFireEv(index, Event::status, reinterpret_cast<int>(recognizerInitErr)); if(rec == nullptr) fireEv(index, Event::status, recognizerInitErr);
else globalPool.exec([this, index]{main(index);}); else globalPool.exec([this, index]{main(index);});
} }
void Recognizer::safeDelete(bool _processCurrent) { void Recognizer::safeDelete(bool _processCurrent) {

View File

@@ -3,12 +3,12 @@
#include "emscripten/wasm_worker.h" #include "emscripten/wasm_worker.h"
#include "emscripten/em_js.h" #include "emscripten/em_js.h"
EM_JS(void, mtFireEv, (int index, int typeIdx, int content), { EM_JS(void, fireEv, (int idx, int typeIdx, const char* content), {
objs[index].dispatchEvent(new CustomEvent(events[typeIdx], { "detail" : content == 0 ? null : UTF8ToString(content) })); if(ENVIRONMENT_IS_WEB) objs[idx].dispatchEvent(new CustomEvent(events[typeIdx], {
"detail": content == 0 ? null : UTF8ToString(content)
}));
else self.postMessage([idx, typeIdx, content]);
}); });
void fireEv(int index, int typeIdx, const char* content) {
emscripten_wasm_worker_post_function_viii(0, mtFireEv, index, typeIdx, reinterpret_cast<int>(content));
}
int untar(unsigned char* tar, int tarSize, const std::string& storepath) { int untar(unsigned char* tar, int tarSize, const std::string& storepath) {
if(std::memcmp(tar + 257, "ustar", 5)) return IncorrectFormat; if(std::memcmp(tar + 257, "ustar", 5)) return IncorrectFormat;
size_t size{}; size_t size{};
@@ -49,7 +49,7 @@ int untar(unsigned char* tar, int tarSize, const std::string& storepath) {
} }
return Successful; return Successful;
} }
static void workerStartup(int _pool) { void workerStartup(int _pool) {
WorkerPool& pool{*reinterpret_cast<WorkerPool*>(_pool)}; WorkerPool& pool{*reinterpret_cast<WorkerPool*>(_pool)};
std::function<void()> fn; std::function<void()> fn;
while(!pool.done) { while(!pool.done) {
@@ -73,15 +73,16 @@ static void workerStartup(int _pool) {
} }
using _startupFn = void(*)(int); using _startupFn = void(*)(int);
EM_JS(void, startupWorkers, (_startupFn startupFn, WorkerPool* pool), { EM_JS(void, startupWorkers, (_startupFn startupFn, WorkerPool* pool), {
Object.values(_wasmWorkers).forEach(worker => { for(let worker of Object.values(_wasmWorkers)) {
worker.postMessage({ worker.postMessage({
"_wsc": startupFn, "_wsc": startupFn,
"x": [ pool ] "x": [ pool ]
}); });
}) worker.onmessage = msg => fireEv(...msg.data);
}
}) })
static constexpr int workerStack{32768}; constexpr int workerStack{32768};
static std::array<std::byte, MAX_WORKERS * workerStack> stacks; std::array<std::byte, MAX_WORKERS * workerStack> stacks;
WorkerPool::WorkerPool() { WorkerPool::WorkerPool() {
for(int i{}; i < MAX_WORKERS; ++i) { for(int i{}; i < MAX_WORKERS; ++i) {
emscripten_create_wasm_worker(&stacks[i * workerStack], workerStack); emscripten_create_wasm_worker(&stacks[i * workerStack], workerStack);
@@ -90,10 +91,14 @@ WorkerPool::WorkerPool() {
} }
#undef MAX_WORKERS #undef MAX_WORKERS
WorkerPool::~WorkerPool() { WorkerPool::~WorkerPool() {
// LTO will remove the EM_JS definition for some reason if it isn't called in the same translation unit (I get undefined symbols), even though it is annotated with EMSCRIPTEN_KEEPALIVE. "Call" it here (this destructor is never called) to workaround that. I'm going to file an issue on Emscripten
fireEv(0, 0);
/*
done = true; done = true;
emscripten_atomic_store_u32(&qLock, false); emscripten_atomic_store_u32(&qLock, false);
emscripten_atomic_notify(&qLock, -1); emscripten_atomic_notify(&qLock, -1);
emscripten_terminate_all_wasm_workers(); emscripten_terminate_all_wasm_workers();
*/
} }
void WorkerPool::exec(std::function<void()> fn) { void WorkerPool::exec(std::function<void()> fn) {
taskQ.emplace(fn); taskQ.emplace(fn);

View File

@@ -41,11 +41,7 @@ struct WorkerPool {
~WorkerPool(); ~WorkerPool();
void exec(std::function<void()> fn); void exec(std::function<void()> fn);
}; };
// Must be called from the main thread extern "C" void fireEv(int idx, int typeIdx, const char* content = nullptr);
extern "C" void mtFireEv(int index, int typeIdx, int content);
// Must be called from a wasm worker
void fireEv(int index, int typeIdx, const char* content = 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

@@ -51,17 +51,18 @@ let processorURL = URL.createObjectURL(new Blob(['(', (() => {
registerProcessor('VoskletTransferer', class extends AudioWorkletProcessor { registerProcessor('VoskletTransferer', class extends AudioWorkletProcessor {
constructor(opts) { constructor(opts) {
super(); super();
this.filledSize = 0; this.filled = 0;
this.bufferSize = opts.processorOptions.bufferSize; this.bufSize = opts.processorOptions[0];
this.buffer = new Float32Array(this.bufferSize); this.buf = new Float32Array(this.bufSize);
} }
process(inputs) { process(inputs) {
if(inputs[0][0]) { if(inputs[0][0]) {
this.buffer.set(inputs[0][0], this.filledSize += 128); this.buf.set(inputs[0][0], this.filled);
if(this.filledSize >= this.bufferSize) { this.filled += 128;
this.filledSize = 0; if(this.filled >= this.bufSize) {
this.port.postMessage(this.buffer, [this.buffer.buffer]); this.filled = 0;
this.buffer = new Float32Array(this.bufferSize); this.port.postMessage(this.buf, [this.buf.buffer]);
this.buf = new Float32Array(this.bufSize);
} }
} }
return true; return true;
@@ -158,14 +159,14 @@ Module = {
storageWorker.terminate(); storageWorker.terminate();
}, },
'createTransferer': async (ctx, bufferSize) => { 'createTransferer': async (ctx, bufSize) => {
await ctx.audioWorklet.addModule(processorURL); await ctx.audioWorklet.addModule(processorURL);
return new AudioWorkletNode(ctx, 'VoskletTransferer', { return new AudioWorkletNode(ctx, 'VoskletTransferer', {
channelCountMode: 'explicit', channelCountMode: 'explicit',
numberOfInputs: 1, numberOfInputs: 1,
numberOfOutputs: 0, numberOfOutputs: 0,
channelCount: 1, channelCount: 1,
processorOptions: { bufferSize: bufferSize } processorOptions: [bufSize]
}); });
}, },