215 lines
8.9 KiB
HTML
215 lines
8.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Kokoro Loader</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 20px;
|
|
line-height: 1.6;
|
|
}
|
|
#status {
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
margin-bottom: 10px;
|
|
}
|
|
#log {
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
background-color: #f9f9f9;
|
|
height: 200px;
|
|
overflow-y: auto;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
}
|
|
.error { color: red; }
|
|
.info { color: blue; }
|
|
.success { color: green; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="status">Loading Kokoro...</div>
|
|
<div id="log"></div>
|
|
|
|
<script type="module">
|
|
// Import the KokoroTTS class from the module
|
|
import { KokoroTTS } from '/js/kokoro-js.js';
|
|
|
|
// Logging function
|
|
function log(message, type = 'info') {
|
|
console.log(message);
|
|
const logElement = document.getElementById('log');
|
|
const entry = document.createElement('div');
|
|
entry.className = type;
|
|
entry.textContent = `[${new Date().toISOString()}] ${message}`;
|
|
logElement.appendChild(entry);
|
|
logElement.scrollTop = logElement.scrollHeight;
|
|
|
|
// Also send to parent
|
|
if (window.parent !== window) {
|
|
window.parent.postMessage({
|
|
type: 'kokoro-log',
|
|
logType: type,
|
|
message: message
|
|
}, '*');
|
|
}
|
|
}
|
|
|
|
// Create a simple loader object to handle the Kokoro instance
|
|
window.KokoroLoader = {
|
|
instance: null,
|
|
kokoroTTS: null,
|
|
initialized: false,
|
|
|
|
// Update progress
|
|
updateProgress: function(progress, message) {
|
|
const progressPercent = Math.round(progress * 100);
|
|
document.getElementById('status').textContent = `${message} (${progressPercent}%)`;
|
|
log(`Progress: ${message} (${progressPercent}%)`);
|
|
|
|
// Only notify parent if progress is valid
|
|
if (progress !== undefined && !isNaN(progress) && window.parent !== window) {
|
|
window.parent.postMessage({
|
|
type: 'kokoro-progress',
|
|
progress: progress,
|
|
message: message
|
|
}, '*');
|
|
}
|
|
},
|
|
|
|
// Initialize Kokoro
|
|
init: async function() {
|
|
try {
|
|
log('Starting Kokoro initialization');
|
|
this.updateProgress(0.1, 'Loading Kokoro library...');
|
|
|
|
// Store the KokoroTTS class
|
|
this.kokoroTTS = KokoroTTS;
|
|
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",
|
|
device: "webgpu",
|
|
progress_callback: (progress) => {
|
|
// 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)}%`);
|
|
}
|
|
});
|
|
|
|
log('Model initialized successfully', 'success');
|
|
this.updateProgress(1.0, 'Kokoro ready');
|
|
this.initialized = true;
|
|
|
|
// 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
|
|
}, '*');
|
|
}
|
|
|
|
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);
|
|
|
|
// Notify parent window
|
|
if (window.parent !== window) {
|
|
log('Notifying parent window of initialization failure');
|
|
window.parent.postMessage({
|
|
type: 'kokoro:ready',
|
|
success: false,
|
|
error: errorMsg
|
|
}, '*');
|
|
}
|
|
|
|
document.getElementById('status').textContent = `Error loading Kokoro: ${errorMsg}`;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Initialize Kokoro when the page loads
|
|
window.addEventListener('load', () => {
|
|
log('Page loaded, initializing Kokoro');
|
|
window.KokoroLoader.init();
|
|
});
|
|
|
|
// Handle messages from parent window
|
|
window.addEventListener('message', function(event) {
|
|
if (event.source !== window.parent) return;
|
|
|
|
const data = event.data;
|
|
|
|
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.initialized || !window.KokoroLoader.instance) {
|
|
log(`Cannot process generation request ${data.id}: Kokoro not loaded`, 'error');
|
|
window.parent.postMessage({
|
|
type: 'kokoro-generated',
|
|
id: data.id,
|
|
success: false,
|
|
error: 'Kokoro not loaded'
|
|
}, '*');
|
|
} else {
|
|
log(`Processing generation request ${data.id}`);
|
|
|
|
// Make the generation asynchronous to avoid freezing the UI
|
|
setTimeout(() => {
|
|
window.KokoroLoader.instance.generate(data.text, { voice: data.voice, speed: data.speed })
|
|
.then(result => {
|
|
log(`Generation successful for request ${data.id}`, 'success');
|
|
|
|
// Convert the result to a proper format for the parent window
|
|
const audio = new Uint8Array(result.buffer);
|
|
|
|
window.parent.postMessage({
|
|
type: 'kokoro-generated',
|
|
id: data.id,
|
|
success: true,
|
|
result: { buffer: audio.buffer }
|
|
}, '*');
|
|
})
|
|
.catch(error => {
|
|
log(`Generation failed for request ${data.id}: ${error.message}`, 'error');
|
|
window.parent.postMessage({
|
|
type: 'kokoro-generated',
|
|
id: data.id,
|
|
success: false,
|
|
error: error.message
|
|
}, '*');
|
|
});
|
|
}, 0);
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|