Revamp event system to not switch from C++ --> JS twice. Fix transferer node offset
This commit is contained in:
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
BIN
Vosklet.wasm
BIN
Vosklet.wasm
Binary file not shown.
@@ -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);
|
||||||
};
|
};
|
||||||
@@ -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) {
|
||||||
|
|||||||
25
src/Util.cc
25
src/Util.cc
@@ -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);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
constexpr int workerStack{32768};
|
||||||
static constexpr int workerStack{32768};
|
std::array<std::byte, MAX_WORKERS * workerStack> stacks;
|
||||||
static 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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user