Spaces:
Running
Running
| 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 }); | |
| } | |
| }); | |