Attempt to fix MacOs #13
This commit is contained in:
229
src/Wrapper.js
229
src/Wrapper.js
@@ -1,166 +1,177 @@
|
||||
let objs = []
|
||||
let events = ["status", "partialResult", "result"]
|
||||
let objs = [];
|
||||
let events = ["status", "partialResult", "result"];
|
||||
let storageWorkerURL = URL.createObjectURL(new Blob(['(', (() => {
|
||||
let txtDecoder = new TextDecoder();
|
||||
let txtEncoder = new TextEncoder();
|
||||
onmessage = async msg => {
|
||||
msg = msg.data;
|
||||
let components = msg.storepath.split("/");
|
||||
let prevDir = await navigator.storage.getDirectory();
|
||||
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) {
|
||||
postMessage("Unable to download model");
|
||||
return;
|
||||
}
|
||||
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(['(', (() => {
|
||||
registerProcessor("VoskletTransferer", class extends AudioWorkletProcessor {
|
||||
constructor(opts) {
|
||||
super()
|
||||
this.count = 0
|
||||
this.maxCount = opts.processorOptions.maxCount
|
||||
this.buffer = new Float32Array(this.maxCount * 128)
|
||||
super();
|
||||
this.count = 0;
|
||||
this.maxCount = opts.processorOptions.maxCount;
|
||||
this.buffer = new Float32Array(this.maxCount * 128);
|
||||
}
|
||||
process(inputs) {
|
||||
if(!inputs[0][0]) return true
|
||||
this.buffer.set(inputs[0][0], this.count * 128)
|
||||
this.count++
|
||||
if(!inputs[0][0]) return true;
|
||||
this.buffer.set(inputs[0][0], this.count++ * 128);
|
||||
if(this.count >= this.maxCount) {
|
||||
this.count = 0
|
||||
this.port.postMessage(this.buffer, [this.buffer.buffer])
|
||||
this.buffer = new Float32Array(this.maxCount * 128)
|
||||
this.count = 0;
|
||||
this.port.postMessage(this.buffer, [this.buffer.buffer]);
|
||||
this.buffer = new Float32Array(this.maxCount * 128);
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
})
|
||||
}).toString(), ')()'], { type : "text/javascript" }))
|
||||
|
||||
}).toString(), ')()'], { type: "text/javascript" }));
|
||||
Module.cleanUp = async () => {
|
||||
for(let obj of objs) await obj.delete()
|
||||
URL.revokeObjectURL(processorURL)
|
||||
for(let obj of objs) await obj.delete();
|
||||
URL.revokeObjectURL(processorURL);
|
||||
URL.revokeObjectURL(storageWorkerURL);
|
||||
storageWorker.terminate();
|
||||
}
|
||||
|
||||
Module.createTransferer = async (ctx, bufferSize) => {
|
||||
await ctx.audioWorklet.addModule(processorURL)
|
||||
await ctx.audioWorklet.addModule(processorURL);
|
||||
return new AudioWorkletNode(ctx, "VoskletTransferer", {
|
||||
channelCountMode : "explicit",
|
||||
numberOfInputs : 1,
|
||||
numberOfOutputs : 0,
|
||||
channelCount : 1,
|
||||
processorOptions : { maxCount: bufferSize / 128 }
|
||||
})
|
||||
}
|
||||
|
||||
getFileHandle = async (path, create = false) => {
|
||||
let components = path.split("/")
|
||||
let prevDir = await navigator.storage.getDirectory()
|
||||
for(let component of components.slice(0, -1)) prevDir = await prevDir.getDirectoryHandle(component, { create : create })
|
||||
return prevDir.getFileHandle(components[components.length - 1], { create : create })
|
||||
channelCountMode: "explicit",
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 0,
|
||||
channelCount: 1,
|
||||
processorOptions: { maxCount: bufferSize / 128 }
|
||||
});
|
||||
}
|
||||
|
||||
class CommonModel extends EventTarget {
|
||||
constructor() {
|
||||
super()
|
||||
objs.push(this)
|
||||
super();
|
||||
objs.push(this);
|
||||
}
|
||||
delete() {
|
||||
this.obj.delete()
|
||||
this.obj.delete();
|
||||
}
|
||||
static async create(url, storepath, id, normalMdl) {
|
||||
let mdl = new CommonModel()
|
||||
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)
|
||||
if(normalMdl) mdl.findWord = word => mdl.obj.findWord(word)
|
||||
resolve(mdl)
|
||||
}
|
||||
else reject(ev.detail)
|
||||
}, { once : true })
|
||||
})
|
||||
let tar
|
||||
try {
|
||||
let dataFile = await (await getFileHandle(storepath + "/model.tgz")).getFile()
|
||||
let idFile = await (await getFileHandle(storepath + "/id")).getFile()
|
||||
if(await idFile.text() != id) throw ""
|
||||
tar = await new Response(dataFile.stream().pipeThrough(new DecompressionStream("gzip"))).arrayBuffer()
|
||||
}
|
||||
catch {
|
||||
try {
|
||||
let res = await fetch(url)
|
||||
if (!res.ok) throw "Unable to download model"
|
||||
let teedBody = res.body.tee()
|
||||
let newDataFile = await (await getFileHandle(storepath + "/model.tgz", true)).createWritable()
|
||||
await newDataFile.write(await new Response(teedBody[0].pipeThrough(new CompressionStream("gzip"))).arrayBuffer())
|
||||
await newDataFile.close()
|
||||
let newIDFile = await (await getFileHandle(storepath + "/id", true)).createWritable()
|
||||
await newIDFile.write(id)
|
||||
await newIDFile.close()
|
||||
tar = await new Response(teedBody[1]).arrayBuffer()
|
||||
}
|
||||
catch(e) {
|
||||
mdl.delete()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
let tarStart = _malloc(tar.byteLength)
|
||||
HEAPU8.set(new Uint8Array(tar), tarStart)
|
||||
mdl.obj = new Module.CommonModel(objs.length - 1, normalMdl, "/" + storepath, id, tarStart, tar.byteLength)
|
||||
return result
|
||||
});
|
||||
storageWorker.addEventListener("message", tar => {
|
||||
tar = tar.data;
|
||||
let tarStart = _malloc(tar.byteLength);
|
||||
HEAPU8.set(new Uint8Array(tar), tarStart);
|
||||
mdl.obj = new Module.CommonModel(objs.length - 1, normalMdl, "/" + storepath, id, tarStart, tar.byteLength);
|
||||
}, { once: true });
|
||||
storageWorker.postMessage({
|
||||
url: url,
|
||||
storepath: storepath,
|
||||
id: id
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Module.createModel = async (url, storepath, id) => {
|
||||
return CommonModel.create(url, storepath, id, true)
|
||||
}
|
||||
Module.createModel = async (url, storepath, id) =>
|
||||
CommonModel.mk(url, storepath, id, true);
|
||||
|
||||
Module.createSpkModel = async (url, storepath, id) => {
|
||||
return CommonModel.create(url, storepath, id, false)
|
||||
}
|
||||
Module.createSpkModel = async (url, storepath, id) =>
|
||||
CommonModel.mk(url, storepath, id, false);
|
||||
|
||||
class Recognizer extends EventTarget {
|
||||
constructor() {
|
||||
super()
|
||||
objs.push(this)
|
||||
super();
|
||||
objs.push(this);
|
||||
return new Proxy(this, {
|
||||
get(self, prop, _) {
|
||||
if(self[prop] == undefined && self.obj[prop] == undefined) return undefined
|
||||
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
|
||||
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;
|
||||
}
|
||||
})
|
||||
}
|
||||
acceptWaveform(audioData) {
|
||||
let start = _malloc(audioData.length * 4)
|
||||
HEAPF32.set(audioData, start / 4)
|
||||
this.obj.acceptWaveform(start, audioData.length)
|
||||
let start = _malloc(audioData.length * 4);
|
||||
HEAPF32.set(audioData, start / 4);
|
||||
this.obj.acceptWaveform(start, audioData.length);
|
||||
}
|
||||
async delete(processCurrent = false) {
|
||||
let result = new Promise((resolve, _) => this.addEventListener("status", _ => {
|
||||
this.obj.delete()
|
||||
resolve()
|
||||
}, { once : true }))
|
||||
this.obj.safeDelete(processCurrent)
|
||||
return result
|
||||
this.obj.delete();
|
||||
resolve();
|
||||
}, { once: true }));
|
||||
this.obj.safeDelete(processCurrent);
|
||||
return result;
|
||||
}
|
||||
static async create(model, sampleRate, mode, grammar, spkModel) {
|
||||
let rec = new Recognizer()
|
||||
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 })
|
||||
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
|
||||
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
|
||||
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
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Module.createRecognizer = (model, sampleRate) =>
|
||||
Recognizer.mk(model.obj, sampleRate, 1);
|
||||
|
||||
Module.createRecognizer = (model, sampleRate) => {
|
||||
return Recognizer.create(model.obj, sampleRate, 1)
|
||||
}
|
||||
Module.createRecognizerWithSpkModel = (model, sampleRate, spkModel) =>
|
||||
Recognizer.mk(model.obj, sampleRate, 2, null, spkModel.obj);
|
||||
|
||||
Module.createRecognizerWithSpkModel = (model, sampleRate, spkModel) => {
|
||||
return Recognizer.create(model.obj, sampleRate, 2, null, spkModel.obj)
|
||||
}
|
||||
|
||||
Module.createRecognizerWithGrm = (model, sampleRate, grammar) => {
|
||||
return Recognizer.create(model.obj, sampleRate, 3, grammar, null)
|
||||
}
|
||||
Module.createRecognizerWithGrm = (model, sampleRate, grammar) =>
|
||||
Recognizer.mk(model.obj, sampleRate, 3, grammar, null);
|
||||
|
||||
Reference in New Issue
Block a user