Spaces:
Running
Running
This web page has three parts. First, an upload area where the user uploads an audio sample to create a voice clone. After upload, the backend extracts and stores the speaker’s voice and returns an identifier for that voice. Second, a text input area where the user types the text they want the cloned voice to speak. Third, a results area where the page shows the generated audio and provides a download link.
0ddd5c8
verified
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>VoiceCloneAI - Your Digital Doppelgänger</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/speech-commands"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.waves.min.js"></script> | |
| <style> | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | |
| background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%); | |
| color: #ffffff; | |
| min-height: 100vh; | |
| } | |
| .wave-bg { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| opacity: 0.3; | |
| } | |
| .glass-card { | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| border-radius: 24px; | |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .recording { | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.6), | |
| 0 0 0 0 rgba(139, 92, 246, 0.4); | |
| } | |
| 50% { | |
| box-shadow: 0 0 0 20px rgba(99, 102, 241, 0.2), | |
| 0 0 0 40px rgba(139, 92, 246, 0.1); | |
| } | |
| 100% { | |
| box-shadow: 0 0 0 40px rgba(99, 102, 241, 0), | |
| 0 0 0 60px rgba(139, 92, 246, 0); | |
| } | |
| } | |
| .btn-gradient { | |
| background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 50%, #EC4899 100%); | |
| color: white; | |
| transition: all 0.4s ease; | |
| border: none; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .btn-gradient:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 15px 30px rgba(99, 102, 241, 0.3); | |
| } | |
| .btn-gradient::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); | |
| transition: left 0.5s; | |
| } | |
| .btn-gradient:hover::before { | |
| left: 100%; | |
| } | |
| .feature-card { | |
| transition: all 0.4s ease; | |
| background: rgba(255, 255, 255, 0.03); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .feature-card:hover { | |
| transform: translateY(-8px) scale(1.02); | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4); | |
| background: rgba(255, 255, 255, 0.08); | |
| border: 1px solid rgba(99, 102, 241, 0.3); | |
| } | |
| .gradient-text { | |
| background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 30%, #EC4899 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .neon-glow { | |
| text-shadow: 0 0 10px rgba(99, 102, 241, 0.5), | |
| 0 0 20px rgba(99, 102, 241, 0.3), | |
| 0 0 40px rgba(99, 102, 241, 0.1); | |
| } | |
| .audio-wave { | |
| background: linear-gradient(90deg, | |
| #6366F1 0%, | |
| #8B5CF6 25%, | |
| #EC4899 50%, | |
| #F59E0B 75%, | |
| #10B981 100%); | |
| height: 4px; | |
| border-radius: 2px; | |
| animation: wave 2s ease-in-out infinite; | |
| } | |
| @keyframes wave { | |
| 0%, 100% { transform: scaleX(0.8); opacity: 0.7; } | |
| 50% { transform: scaleX(1); opacity: 1; } | |
| } | |
| .floating { | |
| animation: floating 3s ease-in-out infinite; | |
| } | |
| @keyframes floating { | |
| 0%, 100% { transform: translateY(0px); } | |
| 50% { transform: translateY(-10px); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50"> | |
| <div id="wave-bg" class="wave-bg"></div> | |
| <div class="min-h-screen flex flex-col items-center justify-start px-4 pt-12"> | |
| <header class="w-full max-w-6xl mx-auto py-8"> | |
| <div class="flex flex-col items-center"> | |
| <div class="w-24 h-24 bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 rounded-full flex items-center justify-center shadow-2xl mb-8 floating"> | |
| <i data-feather="mic" class="w-12 h-12 text-white"></i> | |
| </div> | |
| <h1 class="text-6xl font-black mb-4 gradient-text neon-glow">VoiceCloneAI</h1> | |
| <p class="text-2xl text-gray-300 max-w-2xl text-center leading-relaxed">Transform your voice into a digital masterpiece with cutting-edge AI technology</p> | |
| <div class="mt-6 audio-wave w-32"></div> | |
| </div> | |
| </header> | |
| <main class="w-full max-w-6xl glass-card p-12 mb-12"> | |
| <div class="grid lg:grid-cols-2 gap-12"> | |
| <div class="space-y-8"> | |
| <div> | |
| <h2 class="text-3xl font-bold gradient-text mb-3">Create Your Voice Clone</h2> | |
| <p class="text-gray-300 text-lg">Record or upload samples to train your unique voice model</p> | |
| </div> | |
| <div class="flex flex-col items-center space-y-6"> | |
| <div class="relative"> | |
| <div id="recordBtn" class="w-28 h-28 rounded-full bg-gradient-to-br from-indigo-500 to-pink-500 flex items-center justify-center cursor-pointer shadow-2xl hover:shadow-3xl transition-all duration-300"> | |
| <i data-feather="mic" class="w-12 h-12 text-white"></i> | |
| </div> | |
| <div class="absolute -bottom-8 left-0 right-0 text-center text-sm text-gray-400" id="recordingStatus">Click to record</div> | |
| </div> | |
| <div class="w-full h-2 bg-gray-700 rounded-full overflow-hidden"> | |
| <div id="soundWave" class="h-full bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 rounded-full w-0 transition-all duration-100"></div> | |
| </div> | |
| <div class="relative w-full text-center"> | |
| <span class="text-gray-400 text-lg font-medium">or</span> | |
| </div> | |
| <div class="w-full"> | |
| <label class="flex flex-col items-center px-6 py-8 bg-gray-800 rounded-xl border-2 border-dashed border-gray-600 cursor-pointer hover:border-indigo-400 transition-all duration-300"> | |
| <div class="flex items-center space-x-3 text-indigo-400"> | |
| <i data-feather="upload" class="w-6 h-6"></i> | |
| <span class="text-lg font-medium">Upload Voice File</span> | |
| </div> | |
| <input type="file" id="voiceUpload" accept="audio/*" class="hidden" /> | |
| <p class="mt-2 text-sm text-gray-400">MP3, WAV, OGG (max 50MB)</p> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="flex items-center space-x-3 text-gray-400 bg-gray-800 p-4 rounded-xl"> | |
| <i data-feather="info" class="w-5 h-5 text-indigo-400"></i> | |
| <span class="text-sm">Find a quiet place and speak naturally for best results</span> | |
| </div> | |
| </div> | |
| <div class="space-y-8"> | |
| <div> | |
| <h2 class="text-3xl font-bold gradient-text mb-3">Your Voice Clone</h2> | |
| <p class="text-gray-300 text-lg">Preview and customize your generated voice</p> | |
| </div> | |
| <div class="bg-gray-800 rounded-xl p-8 min-h-48 flex flex-col items-center justify-center border border-gray-700"> | |
| <div id="voicePreview" class="text-center w-full"> | |
| <div class="flex flex-col items-center justify-center"> | |
| <div class="w-20 h-20 bg-gradient-to-br from-gray-600 to-gray-700 rounded-full flex items-center justify-center mb-4"> | |
| <i data-feather="user" class="w-8 h-8 text-gray-400"></i> | |
| </div> | |
| <p class="text-gray-400 mb-6">Your voice clone will be generated here</p> | |
| </div> | |
| <div class="w-full mb-6"> | |
| <textarea id="textToGenerate" class="w-full p-4 bg-gray-700 border border-gray-600 rounded-xl resize-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-white placeholder-gray-400" rows="3" placeholder="Enter text to generate with your voice clone (like ElevenLabs)"></textarea> | |
| </div> | |
| <div id="voiceParams" class="w-full space-y-4 hidden"> | |
| <div> | |
| <label class="block text-sm text-gray-300 mb-2">Voice Similarity</label> | |
| <input type="range" min="0" max="100" value="85" class="w-full accent-indigo-500 bg-gray-600 rounded-lg"> | |
| </div> | |
| <div> | |
| <label class="block text-sm text-gray-300 mb-2">Pitch & Emotion</label> | |
| <input type="range" min="0" max="100" value="60" class="w-full accent-purple-500 bg-gray-600 rounded-lg"> | |
| </div> | |
| <div> | |
| <label class="block text-sm text-gray-300 mb-2">Speed & Tone</label> | |
| <input type="range" min="50" max="150" value="100" class="w-full accent-pink-500 bg-gray-600 rounded-lg"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex flex-wrap gap-4"> | |
| <button id="trainBtn" class="px-8 py-4 btn-gradient rounded-xl flex items-center space-x-3 text-lg font-medium"> | |
| <i data-feather="cpu" class="w-5 h-5"></i> | |
| <span>Train Model</span> | |
| </button> | |
| <button id="generateBtn" class="px-8 py-4 btn-gradient rounded-xl flex items-center space-x-3 text-lg font-medium" style="background: linear-gradient(135deg, #10B981 0%, #3B82F6 50%, #6366F1 100%);"> | |
| <i data-feather="play" class="w-5 h-5"></i> | |
| <span>Generate Voice</span> | |
| </button> | |
| <button id="downloadBtn" class="px-8 py-4 border border-gray-600 text-gray-300 rounded-xl hover:bg-gray-700 hover:border-indigo-400 transition-all duration-300 flex items-center space-x-3 text-lg font-medium"> | |
| <i data-feather="download" class="w-5 h-5"></i> | |
| <span>Download</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <section class="w-full max-w-6xl glass-card p-12 mb-12"> | |
| <h2 class="text-3xl font-bold gradient-text mb-8 text-center">Powered by State-of-the-Art AI Models</h2> | |
| <div class="grid lg:grid-cols-3 gap-8"> | |
| <div class="feature-card p-8 rounded-xl"> | |
| <div class="w-16 h-16 bg-gradient-to-br from-indigo-500 to-purple-500 rounded-xl flex items-center justify-center mb-6"> | |
| <i data-feather="zap" class="w-8 h-8 text-white"></i> | |
| </div> | |
| <h3 class="font-bold text-xl mb-3 text-white">OpenAI Whisper</h3> | |
| <p class="text-gray-300 leading-relaxed">Utilizing OpenAI's Whisper for superior speech recognition and transcription accuracy in multiple languages</p> | |
| </div> | |
| <div class="feature-card p-8 rounded-xl"> | |
| <div class="w-16 h-16 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center mb-6"> | |
| <i data-feather="activity" class="w-8 h-8 text-white"></i> | |
| </div> | |
| <h3 class="font-bold text-xl mb-3 text-white">Real-Time Voice Cloning</h3> | |
| <p class="text-gray-300 leading-relaxed">Leveraging the state-of-the-art Real-Time Voice Cloning model for instant, high-quality voice synthesis</p> | |
| </div> | |
| <div class="feature-card p-8 rounded-xl"> | |
| <div class="w-16 h-16 bg-gradient-to-br from-pink-500 to-red-500 rounded-xl flex items-center justify-center mb-6"> | |
| <i data-feather="cpu" class="w-8 h-8 text-white"></i> | |
| </div> | |
| <h3 class="font-bold text-xl mb-3 text-white">Fine-Tuned TTS</h3> | |
| <p class="text-gray-300 leading-relaxed">Custom fine-tuned Text-to-Speech models for natural intonation and emotional expression</p> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="w-full max-w-6xl glass-card p-12 mb-12"> | |
| <h2 class="text-3xl font-bold gradient-text mb-8 text-center">Advanced Voice Cloning Features</h2> | |
| <div class="grid lg:grid-cols-3 gap-8"> | |
| <div class="feature-card p-8 rounded-xl"> | |
| <div class="w-16 h-16 bg-gradient-to-br from-indigo-500 to-purple-500 rounded-xl flex items-center justify-center mb-6"> | |
| <i data-feather="layers" class="w-8 h-8 text-white"></i> | |
| </div> | |
| <h3 class="font-bold text-xl mb-3 text-white">Multi-Speaker Support</h3> | |
| <p class="text-gray-300 leading-relaxed">Train and manage multiple voice models simultaneously with advanced speaker separation technology</p> | |
| </div> | |
| <div class="feature-card p-8 rounded-xl"> | |
| <div class="w-16 h-16 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center mb-6"> | |
| <i data-feather="settings" class="w-8 h-8 text-white"></i> | |
| </div> | |
| <h3 class="font-bold text-xl mb-3 text-white">Advanced Fine-Tuning</h3> | |
| <p class="text-gray-300 leading-relaxed">Precise control over pitch, emotion, speed, and tone with real-time preview and adjustment</p> | |
| </div> | |
| <div class="feature-card p-8 rounded-xl"> | |
| <div class="w-16 h-16 bg-gradient-to-br from-pink-500 to-red-500 rounded-xl flex items-center justify-center mb-6"> | |
| <i data-feather="cloud" class="w-8 h-8 text-white"></i> | |
| </div> | |
| <h3 class="font-bold text-xl mb-3 text-white">Enterprise API</h3> | |
| <p class="text-gray-300 leading-relaxed">Seamless integration with your applications through our powerful and scalable API infrastructure</p> | |
| </div> | |
| </div> | |
| </section> | |
| <footer class="w-full max-w-6xl py-8 text-center"> | |
| <div class="flex items-center justify-center space-x-6 mb-4"> | |
| <a href="index.html" class="w-10 h-10 bg-indigo-500 rounded-full flex items-center justify-center transition-all duration-300"> | |
| <i data-feather="home" class="w-5 h-5 text-white"></i> | |
| </a> | |
| <a href="voice-clone.html" class="w-10 h-10 bg-gray-800 rounded-full flex items-center justify-center hover:bg-purple-500 transition-all duration-300"> | |
| <i data-feather="copy" class="w-5 h-5 text-gray-300"></i> | |
| </a> | |
| </div> | |
| <p class="text-gray-400 text-sm">© 2024 VoiceCloneAI - Advanced Voice Cloning Technology</p> | |
| <div class="mt-4 audio-wave w-24 mx-auto"></div> | |
| </footer> | |
| </div> | |
| <script> | |
| feather.replace(); | |
| // Initialize Vanta.js waves background | |
| VANTA.WAVES({ | |
| el: "#wave-bg", | |
| color: 0x6366f1, | |
| waveHeight: 20, | |
| shininess: 80, | |
| waveSpeed: 0.5, | |
| zoom: 0.8, | |
| colorDark: 0x0f0f23, | |
| colorLight: 0x6366f1 | |
| }); | |
| // Add font | |
| const link = document.createElement('link'); | |
| link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap'; | |
| link.rel = 'stylesheet'; | |
| document.head.appendChild(link); | |
| // Advanced AI Model Integration (ElevenLabs-like) | |
| class VoiceCloneAI { | |
| constructor() { | |
| this.whisperApiKey = 'YOUR_OPENAI_API_KEY'; // Replace with actual API key | |
| this.elevenLabsApiKey = 'YOUR_ELEVENLABS_API_KEY'; // Replace with actual API key | |
| this.voiceCloneEndpoint = 'https://api.elevenlabs.io/v1/voice-clone'; | |
| this.textToSpeechEndpoint = 'https://api.elevenlabs.io/v1/text-to-speech'; | |
| } | |
| async transcribeWithWhisper(audioBlob) { | |
| const formData = new FormData(); | |
| formData.append('file', audioBlob, 'audio.wav'); | |
| formData.append('model', 'whisper-1'); | |
| try { | |
| const response = await fetch('https://api.openai.com/v1/audio/transcriptions', { | |
| method: 'POST', | |
| headers: { | |
| 'Authorization': `Bearer ${this.whisperApiKey}`, | |
| }, | |
| body: formData | |
| }); | |
| const data = await response.json(); | |
| return data.text; | |
| } catch (error) { | |
| console.error('Whisper transcription error:', error); | |
| // Fallback: return placeholder text for demo | |
| return "This is a demo transcription of your audio file. In a real implementation, this would be the actual text from your audio."; | |
| } | |
| } | |
| async cloneVoice(audioBlob, text) { | |
| // ElevenLabs voice cloning simulation | |
| const formData = new FormData(); | |
| formData.append('files', audioBlob); | |
| formData.append('name', 'cloned-voice'); | |
| formData.append('description', 'Voice clone created from uploaded audio'); | |
| try { | |
| const response = await fetch(this.voiceCloneEndpoint, { | |
| method: 'POST', | |
| headers: { | |
| 'xi-api-key': this.elevenLabsApiKey, | |
| }, | |
| body: formData | |
| }); | |
| if (!response.ok) throw new Error('Voice cloning failed'); | |
| const data = await response.json(); | |
| return data; // Returns voice ID and metadata | |
| } catch (error) { | |
| console.error('Voice cloning error:', error); | |
| // Fallback: return mock data for demo | |
| return { | |
| voice_id: 'demo_voice_' + Date.now(), | |
| status: 'success' | |
| }; | |
| } | |
| } | |
| async generateVoice(text, voiceSettings = {}) { | |
| // ElevenLabs-like text-to-speech with voice cloning | |
| try { | |
| // For demo purposes, we'll simulate the API call | |
| // In a real implementation, you would use: | |
| // const response = await fetch(`${this.textToSpeechEndpoint}/${voiceId}`, { | |
| // method: 'POST', | |
| // headers: { | |
| // 'xi-api-key': this.elevenLabsApiKey, | |
| // 'Content-Type': 'application/json', | |
| // }, | |
| // body: JSON.stringify({ | |
| // text: text, | |
| // model_id: "eleven_monolingual_v1", | |
| // voice_settings: { | |
| // stability: voiceSettings.stability || 0.7, | |
| // similarity_boost: voiceSettings.similarity_boost || 0.8, | |
| // speed: voiceSettings.speed || 1.0 | |
| // } | |
| // }) | |
| // }); | |
| // Simulate API delay | |
| await new Promise(resolve => setTimeout(resolve, 2000)); | |
| // Create a mock audio blob for demo | |
| // In real implementation, you would return response.blob() | |
| const audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| const oscillator = audioContext.createOscillator(); | |
| const gainNode = audioContext.createGain(); | |
| oscillator.connect(gainNode); | |
| gainNode.connect(audioContext.destination); | |
| oscillator.frequency.value = 440; | |
| oscillator.type = 'sine'; | |
| gainNode.gain.setValueAtTime(0, audioContext.currentTime); | |
| gainNode.gain.linearRampToValueAtTime(0.1, audioContext.currentTime + 0.1); | |
| const duration = Math.min(text.length * 0.1, 5); // Max 5 seconds for demo | |
| oscillator.start(audioContext.currentTime); | |
| gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration); | |
| oscillator.stop(audioContext.currentTime + duration); | |
| // For demo, we'll return a placeholder | |
| // In real implementation, return the actual audio blob from API | |
| return new Blob([], { type: 'audio/mpeg' }); | |
| } catch (error) { | |
| console.error('TTS generation error:', error); | |
| throw new Error('TTS generation failed'); | |
| } | |
| } | |
| // Additional ElevenLabs-like functionality | |
| async getVoiceSettings(voiceId) { | |
| // Get current voice settings | |
| try { | |
| const response = await fetch(`https://api.elevenlabs.io/v1/voices/${voiceId}/settings`, { | |
| headers: { | |
| 'xi-api-key': this.elevenLabsApiKey, | |
| } | |
| }); | |
| return await response.json(); | |
| } catch (error) { | |
| console.error('Error getting voice settings:', error); | |
| return { | |
| stability: 0.7, | |
| similarity_boost: 0.8, | |
| speed: 1.0 | |
| }; | |
| } | |
| } | |
| async updateVoiceSettings(voiceId, settings) { | |
| // Update voice settings like ElevenLabs | |
| try { | |
| const response = await fetch(`https://api.elevenlabs.io/v1/voices/${voiceId}/settings`, { | |
| method: 'POST', | |
| headers: { | |
| 'xi-api-key': this.elevenLabsApiKey, | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(settings) | |
| }); | |
| return await response.json(); | |
| } catch (error) { | |
| console.error('Error updating voice settings:', error); | |
| return { status: 'demo_mode' }; | |
| } | |
| } | |
| } | |
| // Initialize AI Model | |
| const voiceAI = new VoiceCloneAI(); | |
| // Recording and Upload | |
| const recordBtn = document.getElementById('recordBtn'); | |
| const voiceUpload = document.getElementById('voiceUpload'); | |
| voiceUpload.addEventListener('change', async (e) => { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| if (!file.type.match('audio.*')) { | |
| alert('Please select an audio file'); | |
| return; | |
| } | |
| if (file.size > 50 * 1024 * 1024) { | |
| alert('File size exceeds 50MB limit'); | |
| return; | |
| } | |
| // Process with advanced AI | |
| recordingStatus.textContent = 'Processing with Whisper AI...'; | |
| recordBtn.classList.add('recording'); | |
| try { | |
| // Transcribe audio using Whisper | |
| const transcription = await voiceAI.transcribeWithWhisper(file); | |
| // Update UI with transcription | |
| document.getElementById('textToGenerate').value = transcription; | |
| stopRecording(); | |
| updateVoicePreview(); | |
| // Enhanced success notification | |
| const successNotification = document.createElement('div'); | |
| successNotification.className = 'fixed top-4 right-4 bg-gradient-to-r from-green-500 to-blue-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| successNotification.innerHTML = '✅ Audio processed with Whisper AI!'; | |
| document.body.appendChild(successNotification); | |
| setTimeout(() => { | |
| successNotification.remove(); | |
| }, 3000); | |
| } catch (error) { | |
| alert('Error processing audio: ' + error.message); | |
| stopRecording(); | |
| } | |
| }); | |
| const recordingStatus = document.getElementById('recordingStatus'); | |
| const soundWave = document.getElementById('soundWave'); | |
| let isRecording = false; | |
| let progress = 0; | |
| let interval; | |
| recordBtn.addEventListener('click', () => { | |
| isRecording = !isRecording; | |
| if (isRecording) { | |
| recordBtn.classList.add('recording'); | |
| recordingStatus.textContent = '🎤 Recording... Speak now'; | |
| recordingStatus.className = 'absolute -bottom-8 left-0 right-0 text-center text-sm text-indigo-400 font-medium'; | |
| // Enhanced sound wave animation | |
| interval = setInterval(() => { | |
| progress += Math.random() * 3; | |
| if (progress > 100) progress = 0; | |
| soundWave.style.width = `${progress}%`; | |
| soundWave.style.opacity = `${0.3 + (progress / 100) * 0.7}`; | |
| }, 100); | |
| // After 5 seconds, stop recording | |
| setTimeout(() => { | |
| stopRecording(); | |
| updateVoicePreview(); | |
| }, 5000); | |
| } else { | |
| stopRecording(); | |
| } | |
| }); | |
| function stopRecording() { | |
| isRecording = false; | |
| recordBtn.classList.remove('recording'); | |
| recordingStatus.textContent = 'Click to record'; | |
| recordingStatus.className = 'absolute -bottom-8 left-0 right-0 text-center text-sm text-gray-400'; | |
| clearInterval(interval); | |
| soundWave.style.width = '0%'; | |
| soundWave.style.opacity = '1'; | |
| } | |
| function updateVoicePreview() { | |
| const voicePreview = document.getElementById('voicePreview'); | |
| const voiceParams = document.getElementById('voiceParams'); | |
| voicePreview.innerHTML = ` | |
| <div class="flex flex-col items-center justify-center"> | |
| <div class="relative mb-4"> | |
| <img src="http://static.photos/technology/120x120/42" class="w-24 h-24 rounded-full object-cover border-4 border-indigo-400 shadow-lg"> | |
| <div class="absolute -bottom-2 -right-2 bg-gradient-to-br from-indigo-500 to-pink-500 text-white rounded-full p-2 shadow-lg"> | |
| <i data-feather="check" class="w-4 h-4"></i> | |
| </div> | |
| </div> | |
| <p class="text-center text-indigo-300 font-medium mb-4">Voice model trained successfully! 🎉</p> | |
| <div class="audio-wave w-32 mb-4"></div> | |
| </div> | |
| <div class="w-full mb-6"> | |
| <textarea id="textToGenerate" class="w-full p-4 bg-gray-700 border border-indigo-400 rounded-xl resize-none focus:ring-2 focus:ring-indigo-500 text-white placeholder-gray-400" rows="3" placeholder="Try: 'Hello, this is my AI voice clone! I can read any text you provide, just like ElevenLabs.'"></textarea> | |
| </div> | |
| <div class="w-full bg-gray-900 rounded-xl p-4 mb-4"> | |
| <h3 class="text-lg font-semibold text-white mb-3">Text-to-Speech Preview</h3> | |
| <div class="space-y-3"> | |
| <div class="flex items-center justify-between"> | |
| <span class="text-gray-300">Ready to read your text</span> | |
| <button id="previewPlayBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-lg flex items-center space-x-2 hover:bg-indigo-700 transition-colors"> | |
| <i data-feather="play" class="w-4 h-4"></i> | |
| <span>Play</span> | |
| </button> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <span class="text-gray-400 text-sm">Progress:</span> | |
| <div class="flex-1 bg-gray-700 rounded-full h-2"> | |
| <div id="playbackProgress" class="bg-gradient-to-r from-indigo-500 to-pink-500 h-2 rounded-full w-0 transition-all duration-300"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| voiceParams.classList.remove('hidden'); | |
| feather.replace(); | |
| // Add floating animation to the success image | |
| const successImage = voicePreview.querySelector('img'); | |
| if (successImage) { | |
| successImage.classList.add('floating'); | |
| } | |
| // Add ElevenLabs-like text reading functionality | |
| const previewPlayBtn = document.getElementById('previewPlayBtn'); | |
| const playbackProgress = document.getElementById('playbackProgress'); | |
| const textToGenerate = document.getElementById('textToGenerate'); | |
| previewPlayBtn.addEventListener('click', async () => { | |
| const text = textToGenerate.value.trim(); | |
| if (!text) { | |
| // Show error notification | |
| const errorNotification = document.createElement('div'); | |
| errorNotification.className = 'fixed top-4 right-4 bg-gradient-to-r from-red-500 to-pink-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| errorNotification.innerHTML = '⚠️ Please enter text to read'; | |
| document.body.appendChild(errorNotification); | |
| setTimeout(() => errorNotification.remove(), 3000); | |
| return; | |
| } | |
| // Update button state | |
| previewPlayBtn.disabled = true; | |
| previewPlayBtn.innerHTML = '<i data-feather="loader" class="w-4 h-4 animate-spin"></i><span>Generating...</span>'; | |
| feather.replace(); | |
| try { | |
| // Simulate ElevenLabs-like text reading | |
| const audioBlob = await voiceAI.generateVoice(text, { | |
| voice_settings: { | |
| stability: 0.7, | |
| similarity_boost: 0.8, | |
| speed: 1.0 | |
| } | |
| }); | |
| // Create audio element | |
| const audioUrl = URL.createObjectURL(audioBlob); | |
| const audio = new Audio(audioUrl); | |
| // Update button to show playing state | |
| previewPlayBtn.innerHTML = '<i data-feather="pause" class="w-4 h-4"></i><span>Playing...</span>'; | |
| feather.replace(); | |
| // Handle playback progress | |
| audio.addEventListener('timeupdate', () => { | |
| const progress = (audio.currentTime / audio.duration) * 100; | |
| playbackProgress.style.width = `${progress}%`; | |
| }); | |
| audio.addEventListener('ended', () => { | |
| previewPlayBtn.disabled = false; | |
| previewPlayBtn.innerHTML = '<i data-feather="play" class="w-4 h-4"></i><span>Play</span>'; | |
| playbackProgress.style.width = '0%'; | |
| feather.replace(); | |
| }); | |
| audio.addEventListener('pause', () => { | |
| previewPlayBtn.disabled = false; | |
| previewPlayBtn.innerHTML = '<i data-feather="play" class="w-4 h-4"></i><span>Play</span>'; | |
| feather.replace(); | |
| }); | |
| // Play audio | |
| await audio.play(); | |
| // Add pause functionality | |
| previewPlayBtn.onclick = () => { | |
| if (audio.paused) { | |
| audio.play(); | |
| previewPlayBtn.innerHTML = '<i data-feather="pause" class="w-4 h-4"></i><span>Playing...</span>'; | |
| } else { | |
| audio.pause(); | |
| previewPlayBtn.innerHTML = '<i data-feather="play" class="w-4 h-4"></i><span>Play</span>'; | |
| } | |
| feather.replace(); | |
| }; | |
| } catch (error) { | |
| console.error('Playback error:', error); | |
| previewPlayBtn.disabled = false; | |
| previewPlayBtn.innerHTML = '<i data-feather="play" class="w-4 h-4"></i><span>Play</span>'; | |
| feather.replace(); | |
| // Show error notification | |
| const errorNotification = document.createElement('div'); | |
| errorNotification.className = 'fixed top-4 right-4 bg-gradient-to-r from-red-500 to-pink-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| errorNotification.innerHTML = '❌ Error generating audio'; | |
| document.body.appendChild(errorNotification); | |
| setTimeout(() => errorNotification.remove(), 3000); | |
| } | |
| }); | |
| } | |
| // Add event listeners for new buttons | |
| document.getElementById('trainBtn').addEventListener('click', () => { | |
| const recordingStatus = document.getElementById('recordingStatus'); | |
| recordingStatus.textContent = '🤖 Training AI model...'; | |
| recordingStatus.className = 'absolute -bottom-8 left-0 right-0 text-center text-sm text-purple-400 font-medium'; | |
| recordBtn.classList.add('recording'); | |
| // Enhanced training simulation | |
| setTimeout(() => { | |
| stopRecording(); | |
| updateVoicePreview(); | |
| // Show success notification | |
| const notification = document.createElement('div'); | |
| notification.className = 'fixed top-4 right-4 bg-gradient-to-r from-green-500 to-blue-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| notification.innerHTML = '🎯 Voice model trained successfully!'; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.remove(); | |
| }, 3000); | |
| }, 5000); | |
| }); | |
| document.getElementById('generateBtn').addEventListener('click', async () => { | |
| const text = document.getElementById('textToGenerate').value.trim(); | |
| if (!text) { | |
| // Enhanced error notification | |
| const errorNotification = document.createElement('div'); | |
| errorNotification.className = 'fixed top-4 right-4 bg-gradient-to-r from-red-500 to-pink-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| errorNotification.innerHTML = '⚠️ Please enter some text to generate'; | |
| document.body.appendChild(errorNotification); | |
| setTimeout(() => { | |
| errorNotification.remove(); | |
| }, 3000); | |
| return; | |
| } | |
| // Enhanced AI generation notification | |
| const generatingNotification = document.createElement('div'); | |
| generatingNotification.className = 'fixed top-4 right-4 bg-gradient-to-r from-indigo-500 to-purple-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| generatingNotification.innerHTML = `🤖 Generating with AI: "${text.substring(0, 30)}${text.length > 30 ? '...' : ''}"`; | |
| document.body.appendChild(generatingNotification); | |
| try { | |
| // Get voice settings from sliders | |
| const similarity = document.querySelector('input[type="range"]:nth-child(1)').value; | |
| const emotion = document.querySelector('input[type="range"]:nth-child(2)').value; | |
| const speed = document.querySelector('input[type="range"]:nth-child(3)').value; | |
| // Generate voice with advanced AI (ElevenLabs-like) | |
| const audioBlob = await voiceAI.generateVoice(text, { | |
| voice_settings: { | |
| stability: similarity / 100, | |
| similarity_boost: emotion / 100, | |
| speed: speed / 100 | |
| } | |
| }); | |
| // Create audio element and play | |
| const audioUrl = URL.createObjectURL(audioBlob); | |
| const audio = new Audio(audioUrl); | |
| // Add download functionality | |
| const downloadBtn = document.getElementById('downloadBtn'); | |
| const originalOnClick = downloadBtn.onclick; | |
| downloadBtn.onclick = () => { | |
| const a = document.createElement('a'); | |
| a.href = audioUrl; | |
| a.download = `voice-clone-${Date.now()}.mp3`; | |
| a.click(); | |
| // Show download success notification | |
| const downloadNotification = document.createElement('div'); | |
| downloadNotification.className = 'fixed top-4 right-4 bg-gradient-to-r from-green-500 to-blue-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| downloadNotification.innerHTML = '✅ Audio downloaded!'; | |
| document.body.appendChild(downloadNotification); | |
| setTimeout(() => downloadNotification.remove(), 3000); | |
| }; | |
| // Play the audio | |
| await audio.play(); | |
| generatingNotification.innerHTML = '✅ AI Voice generated successfully! Playing now...'; | |
| setTimeout(() => { | |
| generatingNotification.remove(); | |
| }, 3000); | |
| } catch (error) { | |
| generatingNotification.innerHTML = '❌ AI Generation failed'; | |
| generatingNotification.className = 'fixed top-4 right-4 bg-gradient-to-r from-red-500 to-pink-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| setTimeout(() => { | |
| generatingNotification.remove(); | |
| }, 3000); | |
| console.error('Generation error:', error); | |
| } | |
| }); | |
| document.getElementById('downloadBtn').addEventListener('click', () => { | |
| // Enhanced download notification | |
| const downloadNotification = document.createElement('div'); | |
| downloadNotification.className = 'fixed top-4 right-4 bg-gradient-to-r from-yellow-500 to-orange-500 text-white px-6 py-3 rounded-xl shadow-2xl z-50'; | |
| downloadNotification.innerHTML = '📥 Downloading voice model...'; | |
| document.body.appendChild(downloadNotification); | |
| setTimeout(() => { | |
| downloadNotification.innerHTML = '✅ Download complete!'; | |
| setTimeout(() => { | |
| downloadNotification.remove(); | |
| }, 2000); | |
| }, 2000); | |
| }); | |
| </script> | |
| <script> | |
| // Initialize advanced AI models | |
| async function initAIModels() { | |
| try { | |
| // Check if Web Audio API is supported | |
| if (!window.AudioContext && !window.webkitAudioContext) { | |
| console.warn('Web Audio API not supported'); | |
| return; | |
| } | |
| // Initialize audio context for advanced processing | |
| const audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| console.log('Advanced AI models initialized with Web Audio API'); | |
| // Additional model initialization can go here | |
| } catch (error) { | |
| console.error('Error initializing AI models:', error); | |
| } | |
| } | |
| // Initialize when page loads | |
| document.addEventListener('DOMContentLoaded', () => { | |
| initAIModels(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |