Fix Kokoro TTS integration issues: Remove API key requirement and ensure system-specific options display correctly
This commit is contained in:
+36
-101
@@ -59,83 +59,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Create a global object to store Kokoro instance
|
||||
// Create a simple loader object to handle the Kokoro instance
|
||||
window.KokoroLoader = {
|
||||
loaded: false,
|
||||
error: null,
|
||||
instance: null,
|
||||
kokoroTTS: null,
|
||||
voices: null,
|
||||
callbacks: [],
|
||||
progress: 0,
|
||||
progressMessage: 'Initializing...',
|
||||
|
||||
// Register a callback for when Kokoro is loaded
|
||||
onLoad: function(callback) {
|
||||
if (this.loaded) {
|
||||
callback(this.instance);
|
||||
} else if (this.error) {
|
||||
callback(null, this.error);
|
||||
} else {
|
||||
this.callbacks.push(callback);
|
||||
}
|
||||
},
|
||||
initialized: false,
|
||||
|
||||
// Update progress
|
||||
updateProgress: function(progress, message) {
|
||||
this.progress = progress;
|
||||
this.progressMessage = message || 'Loading...';
|
||||
const progressPercent = Math.round(progress * 100);
|
||||
document.getElementById('status').textContent = `${this.progressMessage} (${isNaN(progressPercent) ? 0 : progressPercent}%)`;
|
||||
log(`Progress: ${this.progressMessage} (${isNaN(progressPercent) ? 0 : progressPercent}%)`);
|
||||
document.getElementById('status').textContent = `${message} (${progressPercent}%)`;
|
||||
log(`Progress: ${message} (${progressPercent}%)`);
|
||||
|
||||
// Notify parent window
|
||||
if (window.parent !== window) {
|
||||
// Only notify parent if progress is valid
|
||||
if (progress !== undefined && !isNaN(progress) && window.parent !== window) {
|
||||
window.parent.postMessage({
|
||||
type: 'kokoro-progress',
|
||||
progress: isNaN(progress) ? 0 : progress,
|
||||
message: this.progressMessage
|
||||
progress: progress,
|
||||
message: message
|
||||
}, '*');
|
||||
}
|
||||
},
|
||||
|
||||
// Get default voices
|
||||
getDefaultVoices: function() {
|
||||
return [
|
||||
// American Female voices
|
||||
{ id: 'af_heart', name: 'Heart', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_daisy', name: 'Daisy', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_soft', name: 'Soft', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_glados', name: 'GLaDOS', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_southern_belle', name: 'Southern Belle', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_dramatic', name: 'Dramatic', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_valley_girl', name: 'Valley Girl', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_british', name: 'British', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_russian', name: 'Russian', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_german', name: 'German', lang: 'en-US', gender: 'female' },
|
||||
{ id: 'af_cheeky_cute', name: 'Cheeky Cute', lang: 'en-US', gender: 'female' },
|
||||
|
||||
// American Male voices
|
||||
{ id: 'am_bruce', name: 'Bruce', lang: 'en-US', gender: 'male' },
|
||||
{ id: 'am_announcer', name: 'Announcer', lang: 'en-US', gender: 'male' },
|
||||
{ id: 'am_radio_host', name: 'Radio Host', lang: 'en-US', gender: 'male' },
|
||||
|
||||
// British Female voices
|
||||
{ id: 'bf_charlotte', name: 'Charlotte', lang: 'en-GB', gender: 'female' },
|
||||
{ id: 'bf_elizabeth', name: 'Elizabeth', lang: 'en-GB', gender: 'female' },
|
||||
{ id: 'bf_lily', name: 'Lily', lang: 'en-GB', gender: 'female' },
|
||||
{ id: 'bf_olivia', name: 'Olivia', lang: 'en-GB', gender: 'female' },
|
||||
{ id: 'bf_victoria', name: 'Victoria', lang: 'en-GB', gender: 'female' },
|
||||
|
||||
// British Male voices
|
||||
{ id: 'bm_william', name: 'William', lang: 'en-GB', gender: 'male' },
|
||||
{ id: 'bm_arthur', name: 'Arthur', lang: 'en-GB', gender: 'male' },
|
||||
{ id: 'bm_george', name: 'George', lang: 'en-GB', gender: 'male' },
|
||||
{ id: 'bm_harry', name: 'Harry', lang: 'en-GB', gender: 'male' },
|
||||
{ id: 'bm_jack', name: 'Jack', lang: 'en-GB', gender: 'male' }
|
||||
];
|
||||
},
|
||||
|
||||
// Initialize Kokoro
|
||||
init: async function() {
|
||||
try {
|
||||
@@ -144,77 +89,56 @@
|
||||
|
||||
// Store the KokoroTTS class
|
||||
this.kokoroTTS = KokoroTTS;
|
||||
log('Kokoro library loaded successfully', 'success');
|
||||
log('Kokoro library loaded', 'success');
|
||||
this.updateProgress(0.3, 'Initializing Kokoro model...');
|
||||
|
||||
// Initialize the model
|
||||
const model_id = "onnx-community/Kokoro-82M-v1.0-ONNX";
|
||||
this.instance = await this.kokoroTTS.from_pretrained(model_id, {
|
||||
dtype: "q8", // Use quantized model for better performance
|
||||
device: "wasm", // Use WebAssembly for compatibility
|
||||
device: "webgpu", // Use WebGL for better performance
|
||||
progress_callback: (progress) => {
|
||||
// Map progress from 0-1 to 30-90
|
||||
// Skip progress updates if progress is NaN/undefined (cache loading)
|
||||
if (progress === undefined || isNaN(progress)) {
|
||||
log('Model loaded from cache', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
// Map progress from 0-1 to 30-90%
|
||||
const mappedProgress = 0.3 + (progress * 0.6);
|
||||
this.updateProgress(mappedProgress, `Loading Kokoro model: ${Math.round(progress * 100)}%`);
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch available voices
|
||||
log('Fetching available voices...');
|
||||
this.updateProgress(0.8, 'Fetching voices...');
|
||||
|
||||
// Use default voices directly since the list_voices method is unreliable
|
||||
log('Using predefined voice list instead of attempting to fetch from model');
|
||||
this.voices = this.getDefaultVoices();
|
||||
log(`Using ${this.voices.length} predefined voices`, 'success');
|
||||
|
||||
log('Testing Kokoro with a simple text');
|
||||
this.updateProgress(0.95, 'Testing Kokoro...');
|
||||
|
||||
// Test with a simple text
|
||||
// Use the first available voice for testing
|
||||
const testVoice = this.voices && this.voices.length > 0 ? this.voices[0].id : 'af_heart';
|
||||
await this.instance.generate('Test', { voice: testVoice });
|
||||
|
||||
log('Kokoro initialized successfully', 'success');
|
||||
this.loaded = true;
|
||||
log('Model initialized successfully', 'success');
|
||||
this.updateProgress(1.0, 'Kokoro ready');
|
||||
this.initialized = true;
|
||||
|
||||
// Notify parent window
|
||||
// Notify parent window of successful initialization
|
||||
if (window.parent !== window) {
|
||||
log('Notifying parent window of successful initialization');
|
||||
window.parent.postMessage({
|
||||
type: 'kokoro-ready',
|
||||
success: true,
|
||||
voices: this.voices
|
||||
type: 'kokoro:ready',
|
||||
success: true
|
||||
}, '*');
|
||||
}
|
||||
|
||||
// Call all callbacks
|
||||
log(`Calling ${this.callbacks.length} registered callbacks`);
|
||||
this.callbacks.forEach(callback => callback(this.instance));
|
||||
|
||||
document.getElementById('status').textContent = 'Kokoro loaded and ready!';
|
||||
} catch (error) {
|
||||
const errorMsg = error.message || 'Unknown error';
|
||||
log(`Error initializing Kokoro: ${errorMsg}`, 'error');
|
||||
console.error('Error initializing Kokoro:', error);
|
||||
this.error = error;
|
||||
|
||||
// Notify parent window
|
||||
if (window.parent !== window) {
|
||||
log('Notifying parent window of initialization failure');
|
||||
window.parent.postMessage({
|
||||
type: 'kokoro-ready',
|
||||
type: 'kokoro:ready',
|
||||
success: false,
|
||||
error: errorMsg
|
||||
}, '*');
|
||||
}
|
||||
|
||||
// Call all callbacks with error
|
||||
log(`Calling ${this.callbacks.length} registered callbacks with error`);
|
||||
this.callbacks.forEach(callback => callback(null, error));
|
||||
|
||||
document.getElementById('status').textContent = `Error loading Kokoro: ${errorMsg}`;
|
||||
}
|
||||
}
|
||||
@@ -232,9 +156,20 @@
|
||||
|
||||
const data = event.data;
|
||||
|
||||
if (data.type === 'kokoro-generate') {
|
||||
if (data.type === 'kokoro:initialize') {
|
||||
// If we're already initialized, just send the ready message
|
||||
if (window.KokoroLoader.initialized) {
|
||||
log('Already initialized, sending ready message');
|
||||
window.parent.postMessage({
|
||||
type: 'kokoro:ready',
|
||||
success: true
|
||||
}, '*');
|
||||
}
|
||||
// Otherwise init() will handle sending the ready message when done
|
||||
}
|
||||
else if (data.type === 'kokoro-generate') {
|
||||
// Generate speech in a non-blocking way
|
||||
if (!window.KokoroLoader.loaded) {
|
||||
if (!window.KokoroLoader.initialized || !window.KokoroLoader.instance) {
|
||||
log(`Cannot process generation request ${data.id}: Kokoro not loaded`, 'error');
|
||||
window.parent.postMessage({
|
||||
type: 'kokoro-generated',
|
||||
|
||||
Reference in New Issue
Block a user