File size: 2,398 Bytes
0626c04 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | import { APP_CONFIG } from './config'
import { configureOrt, createTtsSessions, ensureWebGpuSupport, fetchCondEmb, fetchTokenizerJson } from './onnx'
import { runFinnishPipeline, type TtsSessions } from './pipeline'
import { createTokenizer, type BrowserTokenizer } from './tokenizer'
import type { WorkerRequest, WorkerResponse } from './types'
let sessionsPromise: Promise<TtsSessions> | null = null
let tokenizerPromise: Promise<BrowserTokenizer> | null = null
let condEmbPromise: Promise<Float32Array> | null = null
const workerScope = self as typeof self & {
postMessage: (message: WorkerResponse, transfer?: Transferable[]) => void
onmessage: ((event: MessageEvent<WorkerRequest>) => void | Promise<void>) | null
}
function postMessageToMain(message: WorkerResponse, transfer: Transferable[] = []): void {
workerScope.postMessage(message, transfer)
}
function reportStatus(message: string): void {
postMessageToMain({ type: 'status', message })
}
async function ensureInitialized(): Promise<{
sessions: TtsSessions
tokenizer: BrowserTokenizer
condEmb: Float32Array
}> {
ensureWebGpuSupport()
configureOrt()
sessionsPromise ??= createTtsSessions(reportStatus)
tokenizerPromise ??= fetchTokenizerJson().then((json) => createTokenizer(json))
condEmbPromise ??= fetchCondEmb()
const [sessions, tokenizer, condEmb] = await Promise.all([
sessionsPromise,
tokenizerPromise,
condEmbPromise,
])
postMessageToMain({ type: 'ready' })
return { sessions, tokenizer, condEmb }
}
async function handleSpeakRequest(message: Extract<WorkerRequest, { type: 'speak' }>): Promise<void> {
const { sessions, tokenizer, condEmb } = await ensureInitialized()
const result = await runFinnishPipeline({
sessions,
tokenizer,
condEmb,
referenceAudio: message.referenceAudio,
text: message.text,
reportStatus,
})
postMessageToMain(
{
type: 'result',
audio: result.audio,
sampleRate: APP_CONFIG.sampleRate,
speechTokenCount: result.speechTokenCount,
},
[result.audio.buffer],
)
}
workerScope.onmessage = async (event: MessageEvent<WorkerRequest>) => {
try {
if (event.data.type === 'speak') {
await handleSpeakRequest(event.data)
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
postMessageToMain({ type: 'error', message })
}
}
|