Remove OPFS, use cache API, map index onto [A-Z] for path. Change to build twice, can't hack anymore :(
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
#### The file Vosklet.js in this folder, used by the examples and the outer [README.md](../README.md), has been set to decompress manually using ```DecompressionStream``` because I can't set a third-party (Github's) server response header. You can utilize this if you run into the same situation. Otherwise, please use the outer Vosklet.js instead.
|
#### The file Vosklet.js in this folder, used by the examples and the outer [README.md](../README.md), has been set to decompress explicitly using ```DecompressionStream``` (instead of implicit browser decompression) because I can't set a third-party (Github's) server response header. You can utilize this if you run into the same situation. Otherwise, please use the outer Vosklet.js instead.
|
||||||
|
|
||||||
#### The motivation is that it will work right away when put into a HTML file. You can just make a local copy and try everything out quickly
|
#### The motivation is that it will work right away when put into a HTML file. You can just make a local copy and try everything out quickly
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
152
Examples/Wrapper.js
Normal file
152
Examples/Wrapper.js
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview
|
||||||
|
* @suppress {undefinedVars|checkTypes}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(ENVIRONMENT_IS_WEB) {
|
||||||
|
|
||||||
|
// 'var' to expose this outside the if
|
||||||
|
var objs = [];
|
||||||
|
var events = ['status', 'partialResult', 'result'];
|
||||||
|
let processorURL = URL.createObjectURL(new Blob(['(', (() => {
|
||||||
|
registerProcessor('VoskletTransferer', class extends AudioWorkletProcessor {
|
||||||
|
constructor(opts) {
|
||||||
|
super();
|
||||||
|
this.filled = 0;
|
||||||
|
this.bufSize = opts.processorOptions[0];
|
||||||
|
this.buf = new Float32Array(this.bufSize);
|
||||||
|
}
|
||||||
|
process(inputs) {
|
||||||
|
if(inputs[0][0]) {
|
||||||
|
this.buf.set(inputs[0][0], this.filled);
|
||||||
|
this.filled += 128;
|
||||||
|
if(this.filled >= this.bufSize) {
|
||||||
|
this.filled = 0;
|
||||||
|
this.port.postMessage(this.buf, [this.buf.buffer]);
|
||||||
|
this.buf = new Float32Array(this.bufSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).toString(), ')()'], { type: 'text/javascript' }));
|
||||||
|
class CommonModel extends EventTarget {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
objs.push(this);
|
||||||
|
}
|
||||||
|
delete() {
|
||||||
|
this.obj.delete();
|
||||||
|
}
|
||||||
|
static async mk(url, storepath, id, normalMdl) {
|
||||||
|
let mdl = new CommonModel();
|
||||||
|
let result = new Promise((resolve, reject) => {
|
||||||
|
mdl.addEventListener('status', ev => {
|
||||||
|
if(!ev.detail) {
|
||||||
|
if(normalMdl) mdl['findWord'] = word => mdl.obj['findWord'](word)
|
||||||
|
resolve(mdl)
|
||||||
|
}
|
||||||
|
else reject(ev.detail)
|
||||||
|
}, { once: true })
|
||||||
|
});
|
||||||
|
let cache = await caches.open('Vosklet');
|
||||||
|
let res = await cache.match(storepath);
|
||||||
|
let tar;
|
||||||
|
if(typeof res == 'undefined' || res.headers.get('id') != id) {
|
||||||
|
// Caching already handled explicitly
|
||||||
|
res = await fetch(url, { cache: 'no-store' });
|
||||||
|
if (!res.ok) throw 'Unable to fetch model, status: ' + res.status;
|
||||||
|
await cache.put(storepath, new Response(
|
||||||
|
res.clone().body, { headers: { 'id': id } }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
tar = await new Response(res.body.pipeThrough(new DecompressionStream('gzip'))).arrayBuffer();
|
||||||
|
let tarStart = _malloc(tar.byteLength);
|
||||||
|
HEAPU8.set(new Uint8Array(tar), tarStart);
|
||||||
|
mdl.obj = new Module['CommonModel'](objs.length - 1, normalMdl, tarStart, tar.byteLength);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Recognizer extends EventTarget {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
// Closure workaround to prevent acceptWaveform from getting removed
|
||||||
|
this['acceptWaveform'] = audioData => {
|
||||||
|
let start = _malloc(audioData.length * 4);
|
||||||
|
HEAPF32.set(audioData, start / 4);
|
||||||
|
this.obj['acceptWaveform'](start, audioData.length);
|
||||||
|
}
|
||||||
|
objs.push(this);
|
||||||
|
return new Proxy(this, {
|
||||||
|
get(self, prop, _) {
|
||||||
|
if(self[prop] == undefined && self.obj[prop] == undefined) return;
|
||||||
|
let p = self[prop];
|
||||||
|
if(p) return p.bind ? p.bind(self) : p;
|
||||||
|
p = self.obj[prop];
|
||||||
|
return p.bind ? p.bind(self.obj) : p;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
async delete(processCurrent = false) {
|
||||||
|
let result = new Promise((resolve, _) => this.addEventListener('status', _ => {
|
||||||
|
this.obj.delete();
|
||||||
|
resolve();
|
||||||
|
}, { once: true }));
|
||||||
|
this.obj['safeDelete'](processCurrent);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static async mk(model, sampleRate, mode, grammar, spkModel) {
|
||||||
|
let rec = new Recognizer();
|
||||||
|
let result = new Promise((resolve, reject) => {
|
||||||
|
rec.addEventListener('status', ev => {
|
||||||
|
if(!ev.detail) resolve(rec);
|
||||||
|
else reject(ev.detail);
|
||||||
|
}, { once: true });
|
||||||
|
})
|
||||||
|
switch(mode) {
|
||||||
|
case 1:
|
||||||
|
rec.obj = new Module['Recognizer'](objs.length - 1, sampleRate, model);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Module = {
|
||||||
|
'cleanUp': async () => {
|
||||||
|
for(let obj of objs) await obj.delete();
|
||||||
|
URL.revokeObjectURL(processorURL);
|
||||||
|
},
|
||||||
|
|
||||||
|
'createTransferer': async (ctx, bufSize) => {
|
||||||
|
await ctx.audioWorklet.addModule(processorURL);
|
||||||
|
return new AudioWorkletNode(ctx, 'VoskletTransferer', {
|
||||||
|
channelCountMode: 'explicit',
|
||||||
|
numberOfInputs: 1,
|
||||||
|
numberOfOutputs: 0,
|
||||||
|
channelCount: 1,
|
||||||
|
processorOptions: [bufSize]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'createModel': (url, storepath, id) =>
|
||||||
|
CommonModel.mk(url, storepath, id, true),
|
||||||
|
|
||||||
|
'createSpkModel': (url, storepath, id) =>
|
||||||
|
CommonModel.mk(url, storepath, id, false),
|
||||||
|
|
||||||
|
'createRecognizer': (model, sampleRate) =>
|
||||||
|
Recognizer.mk(model.obj, sampleRate, 1),
|
||||||
|
|
||||||
|
'createRecognizerWithGrm': (model, sampleRate, grammar) =>
|
||||||
|
Recognizer.mk(model.obj, sampleRate, 3, grammar, null),
|
||||||
|
|
||||||
|
'createRecognizerWithSpkModel': (model, sampleRate, spkModel) =>
|
||||||
|
Recognizer.mk(model.obj, sampleRate, 2, null, spkModel.obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,19 +5,19 @@
|
|||||||
<script>
|
<script>
|
||||||
async function start() {
|
async function start() {
|
||||||
// All data is collected and transfered to the main thread so the AudioContext won't output anything. Set sinkId type to none to save power
|
// All data is collected and transfered to the main thread so the AudioContext won't output anything. Set sinkId type to none to save power
|
||||||
let ctx = new AudioContext({sinkId: {type: "none"}})
|
let ctx = new AudioContext({sinkId: {type: "none"}});
|
||||||
let module = await loadVosklet()
|
let module = await loadVosklet();
|
||||||
let model = await module.createModel("https://ccoreilly.github.io/vosk-browser/models/vosk-model-small-en-us-0.15.tar.gz", "English", "vosk-model-small-en-us-0.15")
|
let model = await module.createModel("https://ccoreilly.github.io/vosk-browser/models/vosk-model-small-en-us-0.15.tar.gz", "English", "vosk-model-small-en-us-0.15")
|
||||||
let recognizer = await module.createRecognizer(model, ctx.sampleRate)
|
let recognizer = await module.createRecognizer(model, ctx.sampleRate);
|
||||||
|
|
||||||
// Listen for result and partial result
|
// Listen for result and partial result
|
||||||
recognizer.addEventListener("result", ev => console.log("Result: ", ev.detail))
|
recognizer.addEventListener("result", ev => console.log("Result: ", ev.detail))
|
||||||
recognizer.addEventListener("partialResult", ev => console.log("Partial result: ", ev.detail))
|
recognizer.addEventListener("partialResult", ev => console.log("Partial result: ", ev.detail));
|
||||||
|
|
||||||
// Fetch, decode, and recognize the .wav
|
// Fetch, decode, and recognize the .wav
|
||||||
let wav = await fetch("https://cdn.jsdelivr.net/gh/msqr1/Vosklet/examples/1to10-en.wav")
|
let wav = await fetch("https://cdn.jsdelivr.net/gh/msqr1/Vosklet/examples/1to10-en.wav");
|
||||||
let audioBuf = await ctx.decodeAudioData(await wav.arrayBuffer())
|
let audioBuf = await ctx.decodeAudioData(await wav.arrayBuffer());
|
||||||
recognizer.acceptWaveform(audioBuf.getChannelData(0))
|
recognizer.acceptWaveform(audioBuf.getChannelData(0));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<script>
|
<script>
|
||||||
async function start() {
|
async function start() {
|
||||||
// All data is collected and transfered to the main thread so the AudioContext won't output anything. Set sinkId type to none to save power
|
// All data is collected and transfered to the main thread so the AudioContext won't output anything. Set sinkId type to none to save power
|
||||||
let ctx = new AudioContext({sinkId: {type: "none"}})
|
let ctx = new AudioContext({sinkId: {type: "none"}});
|
||||||
|
|
||||||
// Setup microphone
|
// Setup microphone
|
||||||
let micNode = ctx.createMediaStreamSource(await navigator.mediaDevices.getUserMedia({
|
let micNode = ctx.createMediaStreamSource(await navigator.mediaDevices.getUserMedia({
|
||||||
@@ -15,25 +15,25 @@
|
|||||||
noiseSuppression: true,
|
noiseSuppression: true,
|
||||||
channelCount: 1
|
channelCount: 1
|
||||||
},
|
},
|
||||||
}))
|
}));
|
||||||
|
|
||||||
// Load Vosklet module, model and recognizer
|
// Load Vosklet module, model and recognizer
|
||||||
let module = await loadVosklet()
|
let module = await loadVosklet();
|
||||||
let model = await module.createModel("https://ccoreilly.github.io/vosk-browser/models/vosk-model-small-en-us-0.15.tar.gz", "English", "vosk-model-small-en-us-0.15")
|
let model = await module.createModel("https://ccoreilly.github.io/vosk-browser/models/vosk-model-small-en-us-0.15.tar.gz", "English", "vosk-model-small-en-us-0.15");
|
||||||
let recognizer = await module.createRecognizer(model, ctx.sampleRate)
|
let recognizer = await module.createRecognizer(model, ctx.sampleRate);
|
||||||
|
|
||||||
// Listen for result and partial result
|
// Listen for result and partial result
|
||||||
recognizer.addEventListener("result", ev => console.log("Result: ", ev.detail))
|
recognizer.addEventListener("result", ev => console.log("Result: ", ev.detail));
|
||||||
recognizer.addEventListener("partialResult", ev => console.log("Partial result: ", ev.detail))
|
recognizer.addEventListener("partialResult", ev => console.log("Partial result: ", ev.detail));
|
||||||
|
|
||||||
// Create a transferer node to get audio data on the main thread
|
// Create a transferer node to get audio data on the main thread
|
||||||
let transferer = await module.createTransferer(ctx, 128 * 150)
|
let transferer = await module.createTransferer(ctx, 128 * 150);
|
||||||
|
|
||||||
// Recognize data on arrival
|
// Recognize data on arrival
|
||||||
transferer.port.onmessage = ev => recognizer.acceptWaveform(ev.data)
|
transferer.port.onmessage = ev => recognizer.acceptWaveform(ev.data);
|
||||||
|
|
||||||
// Connect transferer to microphone
|
// Connect transferer to microphone
|
||||||
micNode.connect(transferer)
|
micNode.connect(transferer);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<!-- Start and create audio context only as a result of user's action -->
|
<!-- Start and create audio context only as a result of user's action -->
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -26,7 +26,7 @@
|
|||||||
<script>
|
<script>
|
||||||
async function start() {
|
async function start() {
|
||||||
// All data is collected and transfered to the main thread so the AudioContext won't output anything. Set sinkId type to none to save power
|
// All data is collected and transfered to the main thread so the AudioContext won't output anything. Set sinkId type to none to save power
|
||||||
let ctx = new AudioContext({sinkId: {type: "none"}})
|
let ctx = new AudioContext({sinkId: {type: "none"}});
|
||||||
|
|
||||||
// Setup microphone
|
// Setup microphone
|
||||||
let micNode = ctx.createMediaStreamSource(await navigator.mediaDevices.getUserMedia({
|
let micNode = ctx.createMediaStreamSource(await navigator.mediaDevices.getUserMedia({
|
||||||
@@ -36,25 +36,25 @@
|
|||||||
noiseSuppression: true,
|
noiseSuppression: true,
|
||||||
channelCount: 1
|
channelCount: 1
|
||||||
},
|
},
|
||||||
}))
|
}));
|
||||||
|
|
||||||
// Load Vosklet module, model and recognizer
|
// Load Vosklet module, model and recognizer
|
||||||
let module = await loadVosklet()
|
let module = await loadVosklet();
|
||||||
let model = await module.createModel("https://ccoreilly.github.io/vosk-browser/models/vosk-model-small-en-us-0.15.tar.gz","English","vosk-model-small-en-us-0.15")
|
let model = await module.createModel("https://ccoreilly.github.io/vosk-browser/models/vosk-model-small-en-us-0.15.tar.gz","English","vosk-model-small-en-us-0.15")
|
||||||
let recognizer = await module.createRecognizer(model, ctx.sampleRate)
|
let recognizer = await module.createRecognizer(model, ctx.sampleRate);
|
||||||
|
|
||||||
// Listen for result and partial result
|
// Listen for result and partial result
|
||||||
recognizer.addEventListener("result", ev => console.log("Result: ", ev.detail))
|
recognizer.addEventListener("result", ev => console.log("Result: ", ev.detail))
|
||||||
recognizer.addEventListener("partialResult", ev => console.log("Partial result: ", ev.detail))
|
recognizer.addEventListener("partialResult", ev => console.log("Partial result: ", ev.detail));
|
||||||
|
|
||||||
// Create a transferer node to get audio data on the main thread
|
// Create a transferer node to get audio data on the main thread
|
||||||
let transferer = await module.createTransferer(ctx, 128 * 150)
|
let transferer = await module.createTransferer(ctx, 128 * 150);
|
||||||
|
|
||||||
// Recognize data on arrival
|
// Recognize data on arrival
|
||||||
transferer.port.onmessage = ev => recognizer.acceptWaveform(ev.data)
|
transferer.port.onmessage = ev => recognizer.acceptWaveform(ev.data);
|
||||||
|
|
||||||
// Connect transferer to microphone
|
// Connect transferer to microphone
|
||||||
micNode.connect(transferer)
|
micNode.connect(transferer);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<!-- Start and create audio context only as a result of user's action -->
|
<!-- Start and create audio context only as a result of user's action -->
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
BIN
Vosklet.wasm
BIN
Vosklet.wasm
Binary file not shown.
@@ -8,28 +8,33 @@ CommonModel::CommonModel(int index, bool normalMdl, int tarStart, int tarSize) :
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
void CommonModel::extractAndLoad(unsigned char* tar, int tarSize) {
|
void CommonModel::extractAndLoad(unsigned char* tar, int tarSize) {
|
||||||
std::string storepath{'/' + std::to_string(index)};
|
// Map index onto [A-Z]
|
||||||
int res{untar(tar, tarSize, storepath.c_str())};
|
const char storepath[3]{'/', static_cast<char>(index % 26 + 'A')};
|
||||||
|
int res{untar(tar, tarSize, storepath)};
|
||||||
free(tar);
|
free(tar);
|
||||||
|
const char* untarErr{};
|
||||||
switch(res) {
|
switch(res) {
|
||||||
case IncorrectFormat:
|
case IncorrectFormat:
|
||||||
fireEv(index, Event::status, "Untar: Incorrect tar format, must be USTAR");
|
untarErr = "Untar: Incorrect tar format, must be USTAR";
|
||||||
return;
|
break;
|
||||||
case IncorrectFiletype:
|
case IncorrectFiletype:
|
||||||
fireEv(index, Event::status, "Untar: Not a directory or regular file");
|
untarErr = "Untar: Not a directory or regular file";
|
||||||
return;
|
break;
|
||||||
case FailedOpen:
|
case FailedOpen:
|
||||||
fireEv(index, Event::status, "Untar: Unable to open file for write");
|
untarErr = "Untar: Unable to open file for write";
|
||||||
return;
|
break;
|
||||||
case FailedWrite:
|
case FailedWrite:
|
||||||
fireEv(index, Event::status, "Untar: Unable to write file");
|
untarErr = "Untar: Unable to write file";
|
||||||
return;
|
break;
|
||||||
case FailedClose:
|
case FailedClose:
|
||||||
fireEv(index, Event::status, "Untar: Unable to close file after write");
|
untarErr = "Untar: Unable to close file after write";
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
if(normalMdl) mdl = vosk_model_new(storepath.c_str());
|
if(untarErr != nullptr) {
|
||||||
else mdl = vosk_spk_model_new(storepath.c_str());
|
fireEv(index, Event::status, untarErr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(normalMdl) mdl = vosk_model_new(storepath);
|
||||||
|
else mdl = vosk_spk_model_new(storepath);
|
||||||
if(normalMdl ? std::get<VoskModel*>(mdl) != nullptr : std::get<VoskSpkModel*>(mdl) != nullptr) fireEv(index, status);
|
if(normalMdl ? std::get<VoskModel*>(mdl) != nullptr : std::get<VoskSpkModel*>(mdl) != nullptr) fireEv(index, status);
|
||||||
else fireEv(index, status, "Unable to load model for recognition");
|
else fireEv(index, status, "Unable to load model for recognition");
|
||||||
fs::remove_all(storepath);
|
fs::remove_all(storepath);
|
||||||
|
|||||||
@@ -5,48 +5,9 @@
|
|||||||
|
|
||||||
if(ENVIRONMENT_IS_WEB) {
|
if(ENVIRONMENT_IS_WEB) {
|
||||||
|
|
||||||
// "var" to expose this outside the if
|
// 'var' to expose this outside the if
|
||||||
var objs = [];
|
var objs = [];
|
||||||
var events = ['status', 'partialResult', 'result'];
|
var events = ['status', 'partialResult', 'result'];
|
||||||
let storageWorkerURL = URL.createObjectURL(new Blob(['(', (async () => {
|
|
||||||
let txtDecoder = new TextDecoder();
|
|
||||||
let txtEncoder = new TextEncoder();
|
|
||||||
let OPFSRoot = await navigator.storage.getDirectory();
|
|
||||||
onmessage = async msg => {
|
|
||||||
msg = msg.data;
|
|
||||||
let components = msg.storepath.split('/');
|
|
||||||
let prevDir = OPFSRoot;
|
|
||||||
for(let component of components) prevDir = await prevDir.getDirectoryHandle(component, { create: true });
|
|
||||||
let idHandle = await prevDir.getFileHandle('id', { create: true });
|
|
||||||
let mdlHandle = await prevDir.getFileHandle('model.tgz', { create: true });
|
|
||||||
let idFile = await idHandle.createSyncAccessHandle();
|
|
||||||
let mdlFile = await mdlHandle.createSyncAccessHandle();
|
|
||||||
let oldIdBuf = new ArrayBuffer(idFile.getSize());
|
|
||||||
idFile.read(oldIdBuf);
|
|
||||||
let tar, tgz;
|
|
||||||
if(txtDecoder.decode(oldIdBuf) == msg.id) {
|
|
||||||
tgz = new ArrayBuffer(mdlFile.getSize());
|
|
||||||
mdlFile.read(tgz);
|
|
||||||
tar = await new Response(new Response(tgz).body.pipeThrough(new DecompressionStream('gzip'))).arrayBuffer();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let res = await fetch(msg.url);
|
|
||||||
if(!res.ok) throw 'Unable to download model'
|
|
||||||
let teed = res.body.tee();
|
|
||||||
tgz = await new Response(teed[0].pipeThrough(new CompressionStream('gzip'))).arrayBuffer();
|
|
||||||
mdlFile.write(tgz, { at: 0 });
|
|
||||||
mdlFile.truncate(tgz.byteLength);
|
|
||||||
let newId = txtEncoder.encode(msg.id);
|
|
||||||
idFile.write(newId, { at: 0 });
|
|
||||||
idFile.truncate(newId.length);
|
|
||||||
tar = await new Response(teed[1]).arrayBuffer();
|
|
||||||
}
|
|
||||||
idFile.close();
|
|
||||||
mdlFile.close();
|
|
||||||
self.postMessage(tar, [tar]);
|
|
||||||
}
|
|
||||||
}).toString(), ')()'], { type: 'text/javascript' }))
|
|
||||||
let storageWorker = new Worker(storageWorkerURL);
|
|
||||||
let processorURL = URL.createObjectURL(new Blob(['(', (() => {
|
let processorURL = URL.createObjectURL(new Blob(['(', (() => {
|
||||||
registerProcessor('VoskletTransferer', class extends AudioWorkletProcessor {
|
registerProcessor('VoskletTransferer', class extends AudioWorkletProcessor {
|
||||||
constructor(opts) {
|
constructor(opts) {
|
||||||
@@ -88,17 +49,23 @@ class CommonModel extends EventTarget {
|
|||||||
else reject(ev.detail)
|
else reject(ev.detail)
|
||||||
}, { once: true })
|
}, { once: true })
|
||||||
});
|
});
|
||||||
storageWorker.addEventListener('message', tar => {
|
let cache = await caches.open('Vosklet');
|
||||||
tar = tar.data;
|
let res = await cache.match(storepath);
|
||||||
|
let tar;
|
||||||
|
if(typeof res == 'undefined' || res.headers.get('id') != id) {
|
||||||
|
// Caching already handled explicitly
|
||||||
|
res = await fetch(url, { cache: 'no-store' });
|
||||||
|
if (!res.ok) throw 'Unable to fetch model, status: ' + res.status;
|
||||||
|
await cache.put(storepath, new Response(
|
||||||
|
res.clone().body.pipeThrough(new CompressionStream('gzip')),
|
||||||
|
{ headers: { 'id': id } }
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
tar = await new Response(res.body.pipeThrough(new DecompressionStream('gzip'))).arrayBuffer();
|
||||||
let tarStart = _malloc(tar.byteLength);
|
let tarStart = _malloc(tar.byteLength);
|
||||||
HEAPU8.set(new Uint8Array(tar), tarStart);
|
HEAPU8.set(new Uint8Array(tar), tarStart);
|
||||||
mdl.obj = new Module['CommonModel'](objs.length - 1, normalMdl, tarStart, tar.byteLength);
|
mdl.obj = new Module['CommonModel'](objs.length - 1, normalMdl, tarStart, tar.byteLength);
|
||||||
}, { once: true });
|
|
||||||
storageWorker.postMessage({
|
|
||||||
url: url,
|
|
||||||
storepath: storepath,
|
|
||||||
id: id
|
|
||||||
});
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,8 +122,6 @@ Module = {
|
|||||||
'cleanUp': async () => {
|
'cleanUp': async () => {
|
||||||
for(let obj of objs) await obj.delete();
|
for(let obj of objs) await obj.delete();
|
||||||
URL.revokeObjectURL(processorURL);
|
URL.revokeObjectURL(processorURL);
|
||||||
URL.revokeObjectURL(storageWorkerURL);
|
|
||||||
storageWorker.terminate();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
'createTransferer': async (ctx, bufSize) => {
|
'createTransferer': async (ctx, bufSize) => {
|
||||||
|
|||||||
19
src/make
19
src/make
@@ -72,15 +72,18 @@ if [ ! -d "$VOSK" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$SRC" &&
|
cd "$SRC" &&
|
||||||
em++ Util.cc CommonModel.cc Recognizer.cc Bindings.cc -O3 -std=c++23 -fno-rtti -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 -DMAX_WORKERS="$MAX_THREADS" -sWASMFS -sWASM_BIGINT -sMODULARIZE -sWASM_EXNREF -sTEXTDECODER=2 -sWASM_WORKERS=2 -sEVAL_CTORS=2 -sINITIAL_MEMORY="$INITIAL_MEMORY" -sALLOW_MEMORY_GROWTH -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sSUPPORT_LONGJMP=0 -sINCOMING_MODULE_JS_API=wasmMemory,instantiateWasm,wasm -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -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 -flto -msimd128 -matomics -mbulk-memory -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js Wrapper.js --closure 1 -o ../Vosklet.js
|
FILES="Util.o CommonModel.o Recognizer.o Bindings.o"
|
||||||
|
COMMON_FLAGS="-O3 -flto -fno-rtti -msimd128 -matomics -mbulk-memory -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals"
|
||||||
|
COMMON_LD_FLAGS="-sWASMFS -sWASM_BIGINT -sMODULARIZE -sTEXTDECODER=2 -sWASM_WORKERS=2 -sEVAL_CTORS=2 -sINITIAL_MEMORY="$INITIAL_MEMORY" -sALLOW_MEMORY_GROWTH -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sSUPPORT_LONGJMP=0 -sINCOMING_MODULE_JS_API=wasmMemory,instantiateWasm,wasm -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -sENVIRONMENT=web,worker -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 --closure 1 --pre-js"
|
||||||
|
em++ ${FILES//.o/.cc} $COMMON_FLAGS -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 -DMAX_WORKERS="$MAX_THREADS" -std=c++23 -c -I. -I"$VOSK"/src &&
|
||||||
|
|
||||||
|
em++ $FILES $COMMON_FLAGS $COMMON_LD_FLAGS Wrapper.js -o ../Vosklet.js
|
||||||
|
em++ $FILES $COMMON_FLAGS $COMMON_LD_FLAGS ../Examples/Wrapper.js -o ../Examples/Vosklet.js
|
||||||
|
|
||||||
|
rm -f $FILES
|
||||||
cd .. &&
|
cd .. &&
|
||||||
|
|
||||||
tr -d '\n' < Vosklet.js | tr -s ' ' > /tmp/hehe && mv /tmp/hehe Vosklet.js &&
|
tr -d '\n' < Vosklet.js | tr -s ' ' > /tmp/hehe && mv /tmp/hehe Vosklet.js &&
|
||||||
|
tr -d '\n' < Examples/Vosklet.js | tr -s ' ' > /tmp/hahe && mv /tmp/hahe Examples/Vosklet.js &&
|
||||||
cp Vosklet.js Examples/Vosklet.js &&
|
|
||||||
cp Vosklet.wasm Examples/Vosklet.wasm &&
|
|
||||||
|
|
||||||
# Can't serve files from raw.githubusercontent with Content-Encoding: gzip header so the browser won't decompress automatically. Manually decompressing instead.
|
|
||||||
sed -i 's/.pipeThrough(new CompressionStream("gzip"))//;s/\[1\])/[1].pipeThrough(new DecompressionStream("gzip")))/' Examples/Vosklet.js &&
|
|
||||||
|
|
||||||
rm -rf /tmp/openfst
|
rm -rf /tmp/openfst
|
||||||
|
|
||||||
|
|||||||
8
test
8
test
@@ -37,13 +37,11 @@ CLAPACK_WASM=$(realpath clapack-wasm)
|
|||||||
|
|
||||||
cd src &&
|
cd src &&
|
||||||
MODE=0 && # 0: Debug, 1: Optimized release
|
MODE=0 && # 0: Debug, 1: Optimized release
|
||||||
|
COMMON_FLAGS="Util.cc CommonModel.cc Recognizer.cc Bindings.cc -std=c++23 -Wall -Wextra -Wpedantic -fno-rtti -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 -DMAX_WORKERS="$MAX_THREADS" -sWASMFS -sWASM_BIGINT -sMODULARIZE -sWASM_EXNREF -sTEXTDECODER=2 -sWASM_WORKERS=2 -sEVAL_CTORS=2 -sINITIAL_MEMORY="$INITIAL_MEMORY" -sALLOW_MEMORY_GROWTH -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sINCOMING_MODULE_JS_API=wasmMemory,instantiateWasm,wasm -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -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 -msimd128 -matomics -mbulk-memory -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js ../Examples/Wrapper.js -o ../test.js"
|
||||||
echo "Mode = $MODE" &&
|
echo "Mode = $MODE" &&
|
||||||
if [ "$MODE" = 0 ]; then
|
if [ "$MODE" = 0 ]; then
|
||||||
em++ Util.cc CommonModel.cc Recognizer.cc Bindings.cc -O0 -std=c++23 -g3 -Wall -Wextra -Wpedantic -fno-rtti -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 -DMAX_WORKERS="$MAX_THREADS" -sWASMFS -sWASM_BIGINT -sMODULARIZE -sWASM_EXNREF -sTEXTDECODER=2 -sWASM_WORKERS=2 -sEVAL_CTORS=2 -sSTACK_OVERFLOW_CHECK=2 -sASSERTIONS=2 -sINITIAL_MEMORY="$INITIAL_MEMORY" -sALLOW_MEMORY_GROWTH -sRUNTIME_DEBUG -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sDISABLE_EXCEPTION_CATCHING=0 -sINCOMING_MODULE_JS_API=wasmMemory,instantiateWasm,wasm -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -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 -msimd128 -matomics -mbulk-memory -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js Wrapper.js -o ../test.js
|
em++ $COMMON_FLAGS -O0 -sRUNTIME_DEBUG -sDISABLE_EXCEPTION_CATCHING=0 -sSTACK_OVERFLOW_CHECK=2 -sASSERTIONS=2 -g3
|
||||||
elif [ "$MODE" = 1 ]; then
|
elif [ "$MODE" = 1 ]; then
|
||||||
em++ Util.cc CommonModel.cc Recognizer.cc Bindings.cc -O3 -Wall -Wextra -Wpedantic -std=c++23 -fno-rtti -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 -DMAX_WORKERS="$MAX_THREADS" -sWASMFS -sWASM_BIGINT -sMODULARIZE -sWASM_EXNREF -sTEXTDECODER=2 -sWASM_WORKERS=2 -sEVAL_CTORS=2 -sINITIAL_MEMORY="$INITIAL_MEMORY" -sALLOW_MEMORY_GROWTH -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sSUPPORT_LONGJMP=0 -sINCOMING_MODULE_JS_API=wasmMemory,instantiateWasm,wasm -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -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 -flto -msimd128 -matomics -mbulk-memory -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js Wrapper.js --closure 1 -o ../test.js &&
|
em++ $COMMON_FLAGS -O3 --closure 1 &&
|
||||||
tr -d '\n' < ../test.js | tr -s ' ' > /tmp/hehe && mv /tmp/hehe ../test.js
|
tr -d '\n' < ../test.js | tr -s ' ' > /tmp/hehe && mv /tmp/hehe ../test.js
|
||||||
fi
|
fi
|
||||||
cd .. &&
|
|
||||||
sed -i 's/.pipeThrough(new CompressionStream("gzip"))//;s/\[1\])/[1].pipeThrough(new DecompressionStream("gzip")))/' test.js &&
|
|
||||||
rm -f test.worker.js
|
|
||||||
Reference in New Issue
Block a user