-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
137 additions
and
126 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) => { | ||
return Recognizer.create(model.obj, sampleRate, 2, null, spkModel.obj) | ||
} | ||
Module.createRecognizerWithSpkModel = (model, sampleRate, spkModel) => | ||
Recognizer.mk(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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.