mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02: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>
This commit is contained in:
113
public/scripts/extensions/tts/kokoro-worker.js
Normal file
113
public/scripts/extensions/tts/kokoro-worker.js
Normal file
@@ -0,0 +1,113 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user