mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-13 02:20:14 +01:00
* feature: 'kokoro-js' supports TTS #3412 * Linting, add credits for kokoro library * Fix voice preview * Fix display languages on previews * Fix settings restoration. Debounce model init on settings change * Fix engine sorting * Move TTS processing to a web worker. Remove unused gain setting * Speaking rate fix * Update status when recreating a worker * Pass voices list from TTS engine * Call dispose function on provider change * Extend worker init timeout to 10 minutes --------- Co-authored-by: ryan <1014670860@qq.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
114 lines
3.1 KiB
JavaScript
114 lines
3.1 KiB
JavaScript
// kokoro-worker.js
|
|
/** @type {import('./lib/kokoro.web.js').KokoroTTS} */
|
|
let tts = null;
|
|
/** @type {boolean} */
|
|
let ready = false;
|
|
/** @type {string[]} */
|
|
let voices = [];
|
|
|
|
// Handle messages from the main thread
|
|
self.onmessage = async function(e) {
|
|
const { action, data } = e.data;
|
|
|
|
switch (action) {
|
|
case 'initialize':
|
|
try {
|
|
const result = await initializeTts(data);
|
|
self.postMessage({
|
|
action: 'initialized',
|
|
success: result,
|
|
voices,
|
|
});
|
|
} catch (error) {
|
|
self.postMessage({
|
|
action: 'initialized',
|
|
success: false,
|
|
error: error.message,
|
|
});
|
|
}
|
|
break;
|
|
|
|
case 'generateTts':
|
|
try {
|
|
const audioBlob = await generateTts(data.text, data.voice, data.speakingRate);
|
|
const blobUrl = URL.createObjectURL(audioBlob);
|
|
self.postMessage({
|
|
action: 'generatedTts',
|
|
success: true,
|
|
blobUrl,
|
|
requestId: data.requestId,
|
|
});
|
|
} catch (error) {
|
|
self.postMessage({
|
|
action: 'generatedTts',
|
|
success: false,
|
|
error: error.message,
|
|
requestId: data.requestId,
|
|
});
|
|
}
|
|
break;
|
|
|
|
case 'checkReady':
|
|
self.postMessage({ action: 'readyStatus', ready });
|
|
break;
|
|
}
|
|
};
|
|
|
|
// Initialize the TTS engine
|
|
async function initializeTts(settings) {
|
|
try {
|
|
const { KokoroTTS } = await import('./lib/kokoro.web.js');
|
|
|
|
console.log('Worker: Initializing Kokoro TTS with settings:', {
|
|
modelId: settings.modelId,
|
|
dtype: settings.dtype,
|
|
device: settings.device,
|
|
});
|
|
|
|
// Create TTS instance
|
|
tts = await KokoroTTS.from_pretrained(settings.modelId, {
|
|
dtype: settings.dtype,
|
|
device: settings.device,
|
|
});
|
|
|
|
// Get available voices
|
|
voices = Object.keys(tts.voices);
|
|
|
|
// Check if generate method exists
|
|
if (typeof tts.generate !== 'function') {
|
|
throw new Error('TTS instance does not have generate method');
|
|
}
|
|
|
|
console.log('Worker: TTS initialized successfully');
|
|
ready = true;
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Worker: Kokoro TTS initialization failed:', error);
|
|
ready = false;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Generate TTS audio
|
|
async function generateTts(text, voiceId, speakingRate) {
|
|
if (!ready || !tts) {
|
|
throw new Error('TTS engine not initialized');
|
|
}
|
|
|
|
if (text.trim().length === 0) {
|
|
throw new Error('Empty text');
|
|
}
|
|
|
|
try {
|
|
const audio = await tts.generate(text, {
|
|
voice: voiceId,
|
|
speed: speakingRate || 1.0,
|
|
});
|
|
|
|
return audio.toBlob();
|
|
} catch (error) {
|
|
console.error('Worker: TTS generation failed:', error);
|
|
throw error;
|
|
}
|
|
}
|