| import WorkerInstance from '$lib/workers/kokoro.worker?worker'; |
|
|
| export class KokoroWorker { |
| private worker: Worker | null = null; |
| private initialized: boolean = false; |
| private dtype: string; |
| private requestQueue: Array<{ |
| text: string; |
| voice: string; |
| resolve: (value: string) => void; |
| reject: (reason: any) => void; |
| }> = []; |
| private processing = false; |
|
|
| constructor(dtype: string = 'fp32') { |
| this.dtype = dtype; |
| } |
|
|
| public async init() { |
| if (this.worker) { |
| console.warn('KokoroWorker is already initialized.'); |
| return; |
| } |
|
|
| this.worker = new WorkerInstance(); |
|
|
| |
| this.worker.onmessage = (event) => { |
| const { status, error, audioUrl } = event.data; |
|
|
| if (status === 'init:complete') { |
| this.initialized = true; |
| } else if (status === 'init:error') { |
| console.error(error); |
| this.initialized = false; |
| } else if (status === 'generate:complete') { |
| |
| const request = this.requestQueue.shift(); |
| if (request) { |
| request.resolve(audioUrl); |
| this.processNextRequest(); |
| } |
| } else if (status === 'generate:error') { |
| const request = this.requestQueue.shift(); |
| if (request) { |
| request.reject(new Error(error)); |
| this.processNextRequest(); |
| } |
| } |
| }; |
|
|
| return new Promise<void>((resolve, reject) => { |
| this.worker!.postMessage({ |
| type: 'init', |
| payload: { dtype: this.dtype } |
| }); |
|
|
| const handleMessage = (event: MessageEvent) => { |
| if (event.data.status === 'init:complete') { |
| this.worker!.removeEventListener('message', handleMessage); |
| this.initialized = true; |
| resolve(); |
| } else if (event.data.status === 'init:error') { |
| this.worker!.removeEventListener('message', handleMessage); |
| reject(new Error(event.data.error)); |
| } |
| }; |
|
|
| this.worker!.addEventListener('message', handleMessage); |
| }); |
| } |
|
|
| public async generate({ text, voice }: { text: string; voice: string }): Promise<string> { |
| if (!this.initialized || !this.worker) { |
| throw new Error('KokoroTTS Worker is not initialized yet.'); |
| } |
|
|
| return new Promise<string>((resolve, reject) => { |
| this.requestQueue.push({ text, voice, resolve, reject }); |
| if (!this.processing) { |
| this.processNextRequest(); |
| } |
| }); |
| } |
|
|
| private processNextRequest() { |
| if (this.requestQueue.length === 0) { |
| this.processing = false; |
| return; |
| } |
|
|
| this.processing = true; |
| const { text, voice } = this.requestQueue[0]; |
| this.worker!.postMessage({ type: 'generate', payload: { text, voice } }); |
| } |
|
|
| public terminate() { |
| if (this.worker) { |
| this.worker.terminate(); |
| this.worker = null; |
| this.initialized = false; |
| this.requestQueue = []; |
| this.processing = false; |
| } |
| } |
| } |
|
|