wasm-streaming-speech / moshiWorker.js
luckyt's picture
Initial commit
0a01d76 verified
import init, {
MoshiASRDecoder,
initThreadPool,
} from "./build/wasm_speech_streaming.js";
async function fetchArrayBuffer(url) {
const cacheName = "whisper-candle-cache";
const cache = await caches.open(cacheName);
const cachedResponse = await cache.match(url);
if (cachedResponse) {
const data = await cachedResponse.arrayBuffer();
return new Uint8Array(data);
}
const res = await fetch(url, { cache: "force-cache" });
cache.put(url, res.clone());
return new Uint8Array(await res.arrayBuffer());
}
class MoshiASR {
static decoder = null;
// Initialize the model
static async initialize(params) {
const { weightsURL, tokenizerURL, mimiURL, configURL } = params;
if (this.decoder) {
self.postMessage({ status: "model_ready" });
return;
}
try {
await init();
const numThreads = navigator.hardwareConcurrency || 4;
await initThreadPool(numThreads);
self.postMessage({
status: "loading",
message: `Loading Model (~950 MB)`,
});
const [weightsArrayU8, tokenizerArrayU8, mimiArrayU8, configArrayU8] =
await Promise.all([
fetchArrayBuffer(weightsURL),
fetchArrayBuffer(tokenizerURL),
fetchArrayBuffer(mimiURL),
fetchArrayBuffer(configURL),
]);
this.decoder = new MoshiASRDecoder(
weightsArrayU8,
tokenizerArrayU8,
mimiArrayU8,
configArrayU8
);
self.postMessage({ status: "model_ready" });
} catch (error) {
self.postMessage({ error: error.message });
}
}
static startStream() {
if (this.decoder) {
this.decoder.start_streaming();
}
}
static stopStream() {
if (this.decoder) {
this.decoder.stop_streaming();
}
}
static processAudio(audioData) {
if (this.decoder) {
this.decoder.process_audio_chunk(audioData, (word) => {
self.postMessage({
status: "streaming",
word: word,
});
});
self.postMessage({
status: "chunk_processed",
});
}
}
}
self.addEventListener("message", async (event) => {
const { command } = event.data;
try {
switch (command) {
case "initialize":
const { weightsURL, modelID, tokenizerURL, mimiURL, configURL } =
event.data;
await MoshiASR.initialize({
weightsURL,
modelID,
tokenizerURL,
mimiURL,
configURL,
});
break;
case "start_stream":
MoshiASR.startStream();
break;
case "stop_stream":
MoshiASR.stopStream();
break;
case "process_audio":
const { audioData } = event.data;
MoshiASR.processAudio(audioData);
break;
default:
self.postMessage({ error: "Unknown command: " + command });
}
} catch (e) {
self.postMessage({ error: e.message });
}
});