voice-over / index.html
theaimoron's picture
Add 3 files
991f11b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FreeVoice | Text to Speech</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Poppins', sans-serif;
background-color: #f8fafc;
}
.gradient-bg {
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
}
.waveform {
background: linear-gradient(90deg, #e0e7ff 0%, #c7d2fe 100%);
height: 60px;
border-radius: 8px;
position: relative;
overflow: hidden;
}
.waveform::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: wave 2s linear infinite;
}
@keyframes wave {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.recording {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(236, 72, 153, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(236, 72, 153, 0); }
100% { box-shadow: 0 0 0 0 rgba(236, 72, 153, 0); }
}
/* Custom audio player styles */
.audio-player {
width: 100%;
margin-top: 10px;
}
.audio-player::-webkit-media-controls-panel {
background-color: #e0e7ff;
border-radius: 8px;
}
.audio-player::-webkit-media-controls-play-button,
.audio-player::-webkit-media-controls-mute-button {
background-color: #6366f1;
border-radius: 50%;
}
/* Custom range slider */
input[type="range"] {
-webkit-appearance: none;
height: 6px;
border-radius: 3px;
background: #e0e7ff;
outline: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #6366f1;
cursor: pointer;
}
/* Voice sample animation */
.voice-sample {
transition: all 0.3s ease;
}
.voice-sample:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.voice-sample.active {
border-color: #6366f1;
background-color: #e0e7ff;
}
</style>
</head>
<body class="bg-gray-50">
<div class="max-w-4xl mx-auto px-4 py-8">
<!-- Text to Speech Section -->
<div class="bg-white shadow rounded-lg p-6 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Text to Speech</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- Text Input -->
<div class="md:col-span-2">
<label for="text-input" class="block text-sm font-medium text-gray-700 mb-1">Enter your text</label>
<textarea id="text-input" rows="8" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="Type or paste your text here...">Welcome to FreeVoice, your free alternative to 11Labs with all premium features unlocked. This text will be converted to speech using our advanced AI technology.</textarea>
<div class="mt-4 flex items-center justify-between">
<div class="flex items-center space-x-4">
<div>
<label for="voice-select" class="block text-sm font-medium text-gray-700 mb-1">Voice</label>
<select id="voice-select" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<option>Rachel (Female, American)</option>
<option>James (Male, British)</option>
<option>Sophie (Female, Australian)</option>
<option>Liam (Male, American)</option>
<option>Emma (Female, Canadian)</option>
<option selected>Oliver (Male, British)</option>
<option>Charlotte (Female, American)</option>
</select>
</div>
<div>
<label for="model-select" class="block text-sm font-medium text-gray-700 mb-1">Model</label>
<select id="model-select" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<option>Standard</option>
<option selected>Premium (High Quality)</option>
<option>Premium Ultra (Highest Quality)</option>
</select>
</div>
</div>
<button id="generate-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-6 py-2 rounded-md text-sm font-medium">
<i class="fas fa-play mr-2"></i>Generate
</button>
</div>
<!-- Voice Samples -->
<div class="mt-6">
<label class="block text-sm font-medium text-gray-700 mb-2">Voice Samples</label>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
<div class="voice-sample border rounded-lg p-3 cursor-pointer" data-voice="Rachel">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-purple-100 flex items-center justify-center mr-3">
<i class="fas fa-female text-purple-600"></i>
</div>
<div>
<div class="font-medium">Rachel</div>
<div class="text-xs text-gray-500">Female, American</div>
</div>
</div>
<audio src="https://actions.google.com/sounds/v1/alarms/beep_short.ogg" preload="auto"></audio>
</div>
<div class="voice-sample border rounded-lg p-3 cursor-pointer" data-voice="James">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center mr-3">
<i class="fas fa-male text-blue-600"></i>
</div>
<div>
<div class="font-medium">James</div>
<div class="text-xs text-gray-500">Male, British</div>
</div>
</div>
<audio src="https://actions.google.com/sounds/v1/alarms/beep_short.ogg" preload="auto"></audio>
</div>
<div class="voice-sample border rounded-lg p-3 cursor-pointer" data-voice="Sophie">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-pink-100 flex items-center justify-center mr-3">
<i class="fas fa-female text-pink-600"></i>
</div>
<div>
<div class="font-medium">Sophie</div>
<div class="text-xs text-gray-500">Female, Australian</div>
</div>
</div>
<audio src="https://actions.google.com/sounds/v1/alarms/beep_short.ogg" preload="auto"></audio>
</div>
<div class="voice-sample border rounded-lg p-3 cursor-pointer" data-voice="Liam">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-green-100 flex items-center justify-center mr-3">
<i class="fas fa-male text-green-600"></i>
</div>
<div>
<div class="font-medium">Liam</div>
<div class="text-xs text-gray-500">Male, American</div>
</div>
</div>
<audio src="https://actions.google.com/sounds/v1/alarms/beep_short.ogg" preload="auto"></audio>
</div>
<div class="voice-sample border rounded-lg p-3 cursor-pointer" data-voice="Emma">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-yellow-100 flex items-center justify-center mr-3">
<i class="fas fa-female text-yellow-600"></i>
</div>
<div>
<div class="font-medium">Emma</div>
<div class="text-xs text-gray-500">Female, Canadian</div>
</div>
</div>
<audio src="https://actions.google.com/sounds/v1/alarms/beep_short.ogg" preload="auto"></audio>
</div>
<div class="voice-sample border rounded-lg p-3 cursor-pointer active" data-voice="Oliver">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center mr-3">
<i class="fas fa-male text-indigo-600"></i>
</div>
<div>
<div class="font-medium">Oliver</div>
<div class="text-xs text-gray-500">Male, British</div>
</div>
</div>
<audio src="https://actions.google.com/sounds/v1/alarms/beep_short.ogg" preload="auto"></audio>
</div>
</div>
</div>
</div>
<!-- Voice Settings -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Voice Settings</label>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="mb-4">
<label for="stability" class="block text-sm font-medium text-gray-700 mb-1">Stability</label>
<input type="range" id="stability" min="0" max="100" value="75" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>More variable</span>
<span>More stable</span>
</div>
</div>
<div class="mb-4">
<label for="clarity" class="block text-sm font-medium text-gray-700 mb-1">Clarity + Similarity</label>
<input type="range" id="clarity" min="0" max="100" value="80" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>More clear</span>
<span>More similar</span>
</div>
</div>
<div>
<label for="style" class="block text-sm font-medium text-gray-700 mb-1">Style Exaggeration</label>
<input type="range" id="style" min="0" max="100" value="50" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>Less style</span>
<span>More style</span>
</div>
</div>
</div>
<!-- Advanced Settings -->
<div class="mt-6">
<label class="block text-sm font-medium text-gray-700 mb-2">Advanced Settings</label>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="mb-4">
<label for="speed" class="block text-sm font-medium text-gray-700 mb-1">Speed</label>
<input type="range" id="speed" min="80" max="120" value="100" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>Slower</span>
<span>Faster</span>
</div>
</div>
<div>
<label for="pitch" class="block text-sm font-medium text-gray-700 mb-1">Pitch</label>
<input type="range" id="pitch" min="80" max="120" value="100" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>Lower</span>
<span>Higher</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Generated Audio -->
<div id="audio-result" class="mt-8 hidden">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-gray-900">Generated Audio</h3>
<div class="flex space-x-2">
<button id="download-btn" class="text-indigo-600 hover:text-indigo-800">
<i class="fas fa-download"></i>
</button>
<button id="share-btn" class="text-indigo-600 hover:text-indigo-800">
<i class="fas fa-share-alt"></i>
</button>
</div>
</div>
<div class="waveform p-4 mb-4">
<!-- Waveform visualization would go here -->
</div>
<audio id="generated-audio" controls class="audio-player w-full">
Your browser does not support the audio element.
</audio>
<div class="flex items-center space-x-4 mt-4">
<button id="play-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md text-sm font-medium">
<i class="fas fa-play mr-2"></i>Play
</button>
<button id="regenerate-btn" class="bg-white hover:bg-gray-50 text-gray-700 px-4 py-2 rounded-md text-sm font-medium border border-gray-300">
<i class="fas fa-redo mr-2"></i>Regenerate
</button>
</div>
</div>
</div>
</div>
<script>
// Voice samples functionality
document.querySelectorAll('.voice-sample').forEach(sample => {
sample.addEventListener('click', function() {
// Remove active class from all samples
document.querySelectorAll('.voice-sample').forEach(s => {
s.classList.remove('active');
});
// Add active class to clicked sample
this.classList.add('active');
// Update voice select dropdown
const voiceName = this.getAttribute('data-voice');
const voiceSelect = document.getElementById('voice-select');
for (let i = 0; i < voiceSelect.options.length; i++) {
if (voiceSelect.options[i].text.includes(voiceName)) {
voiceSelect.selectedIndex = i;
break;
}
}
// Play sample audio
const audio = this.querySelector('audio');
audio.currentTime = 0;
audio.play();
});
});
// Text to Speech Functionality using Web Speech API
document.getElementById('generate-btn').addEventListener('click', function() {
const text = document.getElementById('text-input').value;
const voice = document.getElementById('voice-select').value;
const model = document.getElementById('model-select').value;
const stability = document.getElementById('stability').value;
const clarity = document.getElementById('clarity').value;
const style = document.getElementById('style').value;
const speed = document.getElementById('speed').value;
const pitch = document.getElementById('pitch').value;
if (!text.trim()) {
alert('Please enter some text to convert to speech');
return;
}
// Show loading state
const btn = this;
const originalText = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Generating...';
btn.disabled = true;
// Simulate API call delay
setTimeout(function() {
// Show result
document.getElementById('audio-result').classList.remove('hidden');
// Create speech synthesis
const utterance = new SpeechSynthesisUtterance(text);
// Set voice properties based on selection
if (voice.includes("Rachel")) {
// Female American voice - clear, professional
utterance.rate = 1.1 * (speed / 100);
utterance.pitch = 1.1 * (pitch / 100);
utterance.lang = 'en-US';
} else if (voice.includes("James")) {
// Male British voice - deep, formal
utterance.rate = 0.95 * (speed / 100);
utterance.pitch = 0.9 * (pitch / 100);
utterance.lang = 'en-GB';
} else if (voice.includes("Sophie")) {
// Female Australian voice - bright, friendly
utterance.rate = 1.05 * (speed / 100);
utterance.pitch = 1.15 * (pitch / 100);
utterance.lang = 'en-AU';
} else if (voice.includes("Liam")) {
// Male American voice - casual, youthful
utterance.rate = 1.0 * (speed / 100);
utterance.pitch = 1.0 * (pitch / 100);
utterance.lang = 'en-US';
} else if (voice.includes("Emma")) {
// Female Canadian voice - warm, soothing
utterance.rate = 0.98 * (speed / 100);
utterance.pitch = 1.05 * (pitch / 100);
utterance.lang = 'en-CA';
} else if (voice.includes("Oliver")) {
// Premium Male British voice - refined, articulate
utterance.rate = 0.9 * (speed / 100);
utterance.pitch = 0.85 * (pitch / 100);
utterance.lang = 'en-GB';
} else if (voice.includes("Charlotte")) {
// Premium Female American voice - elegant, expressive
utterance.rate = 1.0 * (speed / 100);
utterance.pitch = 1.2 * (pitch / 100);
utterance.lang = 'en-US';
}
// Adjust based on advanced settings
utterance.rate *= (1 + (50 - clarity) / 100);
utterance.pitch *= (1 + (style - 50) / 100);
// Create audio blob from speech synthesis
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const dest = audioCtx.createMediaStreamDestination();
const mediaRecorder = new MediaRecorder(dest.stream);
let chunks = [];
mediaRecorder.ondataavailable = function(evt) {
chunks.push(evt.data);
};
mediaRecorder.onstop = function() {
const blob = new Blob(chunks, { type: 'audio/wav' });
const audioUrl = URL.createObjectURL(blob);
const audio = document.getElementById('generated-audio');
audio.src = audioUrl;
// Reset button
btn.innerHTML = originalText;
btn.disabled = false;
};
mediaRecorder.start();
// Speak the text
speechSynthesis.speak(utterance);
// Stop recording after speech ends
utterance.onend = function() {
mediaRecorder.stop();
};
// Log generation details
console.log('Generated speech with:', {
text: text,
voice: voice,
model: model,
stability: stability,
clarity: clarity,
style: style,
speed: speed,
pitch: pitch
});
}, 1500);
});
// Play/Stop audio
document.getElementById('play-btn').addEventListener('click', function() {
const audio = document.getElementById('generated-audio');
const icon = this.querySelector('i');
if (audio.paused) {
audio.play();
icon.classList.remove('fa-play');
icon.classList.add('fa-stop');
this.innerHTML = '<i class="fas fa-stop mr-2"></i>Stop';
} else {
audio.pause();
audio.currentTime = 0;
icon.classList.remove('fa-stop');
icon.classList.add('fa-play');
this.innerHTML = '<i class="fas fa-play mr-2"></i>Play';
}
});
// Regenerate audio
document.getElementById('regenerate-btn').addEventListener('click', function() {
document.getElementById('generate-btn').click();
});
// Download audio
document.getElementById('download-btn').addEventListener('click', function() {
const audio = document.getElementById('generated-audio');
const a = document.createElement('a');
a.href = audio.src;
// Get the selected voice name for the filename
const voiceSelect = document.getElementById('voice-select');
const voiceName = voiceSelect.options[voiceSelect.selectedIndex].text
.replace(/[^a-zA-Z0-9]/g, '_')
.toLowerCase();
a.download = `freevoice_${voiceName}.wav`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
// Share audio
document.getElementById('share-btn').addEventListener('click', function() {
if (navigator.share) {
navigator.share({
title: 'FreeVoice Text-to-Speech',
text: 'Check out this AI-generated voice from FreeVoice!',
url: window.location.href
}).catch(err => {
console.log('Error sharing:', err);
alert('Sharing failed: ' + err.message);
});
} else {
alert('Web Share API not supported in your browser. Copy this link to share: ' + window.location.href);
}
});
// Update audio player when audio is playing/paused
document.getElementById('generated-audio').addEventListener('play', function() {
document.getElementById('play-btn').innerHTML = '<i class="fas fa-stop mr-2"></i>Stop';
});
document.getElementById('generated-audio').addEventListener('pause', function() {
if (this.currentTime === 0 || this.ended) {
document.getElementById('play-btn').innerHTML = '<i class="fas fa-play mr-2"></i>Play';
}
});
document.getElementById('generated-audio').addEventListener('ended', function() {
document.getElementById('play-btn').innerHTML = '<i class="fas fa-play mr-2"></i>Play';
});
// Initialize range sliders to show values
const sliders = ['stability', 'clarity', 'style', 'speed', 'pitch'];
sliders.forEach(sliderId => {
const slider = document.getElementById(sliderId);
slider.addEventListener('input', function() {
// You could add visual feedback for the slider values if needed
console.log(`${sliderId} value:`, this.value);
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=theaimoron/voice-over" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>