Darwin-TTS-1.7B-Cross / index.html
SeaWolf-AI's picture
Update index.html
42114bb verified
Raw
History Blame Contribute Delete
19.1 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🧬 Darwin-TTS-1.7B-Cross</title>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Plus Jakarta Sans', -apple-system, sans-serif; background: linear-gradient(135deg, #FFF8F0 0%, #FFF0E6 30%, #F0F4FF 70%, #F8F0FF 100%); min-height: 100vh; color: #2D2B3D; }
.bg-orbs { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; }
.orb { position: absolute; border-radius: 50%; filter: blur(80px); opacity: 0.15; animation: float 20s ease-in-out infinite; }
.orb-1 { width: 400px; height: 400px; background: #FF6B35; top: -100px; right: -100px; }
.orb-2 { width: 350px; height: 350px; background: #7C5CFC; bottom: -50px; left: -50px; animation-delay: -7s; }
.orb-3 { width: 300px; height: 300px; background: #00C9A7; top: 50%; left: 40%; animation-delay: -14s; }
@keyframes float { 0%, 100% { transform: translate(0, 0) scale(1); } 33% { transform: translate(30px, -30px) scale(1.05); } 66% { transform: translate(-20px, 20px) scale(0.95); } }
.container { max-width: 800px; margin: 0 auto; padding: 32px 20px; position: relative; z-index: 1; }
.header { text-align: center; margin-bottom: 36px; }
.badge-link { text-decoration: none; }
.badge { display: inline-flex; align-items: center; gap: 6px; background: linear-gradient(135deg, #FF6B35, #FF8F5E); color: white; font-size: 11px; font-weight: 700; letter-spacing: 1.5px; padding: 6px 16px; border-radius: 20px; text-transform: uppercase; margin-bottom: 16px; box-shadow: 0 4px 15px rgba(255,107,53,0.3); transition: transform 0.2s, box-shadow 0.2s; cursor: pointer; }
.badge:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255,107,53,0.4); }
.header h1 { font-size: 36px; font-weight: 800; line-height: 1.2; background: linear-gradient(135deg, #2D2B3D 0%, #7C5CFC 50%, #FF6B35 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 12px; }
.header p { font-size: 16px; color: #6B6880; max-width: 600px; margin: 0 auto; line-height: 1.6; }
.header-links { display: flex; justify-content: center; gap: 12px; margin-top: 16px; flex-wrap: wrap; }
.header-links a { display: inline-flex; align-items: center; gap: 6px; padding: 8px 18px; border-radius: 12px; font-size: 13px; font-weight: 600; text-decoration: none; transition: all 0.2s; }
.link-model { background: #7C5CFC; color: white; box-shadow: 0 3px 12px rgba(124,92,252,0.3); }
.link-model:hover { transform: translateY(-1px); box-shadow: 0 5px 18px rgba(124,92,252,0.4); }
.link-blog { background: white; color: #7C5CFC; border: 2px solid #E8E6F0; }
.link-blog:hover { border-color: #7C5CFC; transform: translateY(-1px); }
.link-article { background: white; color: #FF6B35; border: 2px solid #FFE0D0; }
.link-article:hover { border-color: #FF6B35; transform: translateY(-1px); }
.card { background: rgba(255,255,255,0.85); backdrop-filter: blur(20px); border: 1px solid rgba(255,255,255,0.9); border-radius: 24px; padding: 32px; box-shadow: 0 8px 40px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04); margin-bottom: 20px; }
.input-group { margin-bottom: 24px; }
.input-group label { display: block; font-size: 13px; font-weight: 700; color: #4A4861; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
textarea { width: 100%; padding: 16px 20px; border: 2px solid #E8E6F0; border-radius: 16px; font-family: inherit; font-size: 16px; resize: vertical; min-height: 80px; transition: all 0.3s; background: #FAFAFE; color: #2D2B3D; }
textarea:focus { outline: none; border-color: #7C5CFC; box-shadow: 0 0 0 4px rgba(124,92,252,0.1); background: white; }
.presets { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 24px; }
.preset-btn { padding: 10px 16px; border: 2px solid #E8E6F0; border-radius: 12px; background: white; cursor: pointer; font-size: 13px; font-weight: 600; color: #6B6880; transition: all 0.2s; flex: 1; min-width: 100px; text-align: center; }
.preset-btn:hover { border-color: #7C5CFC; color: #7C5CFC; transform: translateY(-1px); }
.preset-btn.active { border-color: #7C5CFC; background: #7C5CFC; color: white; }
.toggle-section { background: linear-gradient(135deg, #F8F5FF, #FFF5F0); border-radius: 16px; padding: 24px; margin-bottom: 24px; border: 1px solid rgba(124,92,252,0.1); }
.toggle-row { display: flex; justify-content: space-between; align-items: center; }
.toggle-info h4 { font-size: 14px; font-weight: 700; color: #4A4861; }
.toggle-info p { font-size: 12px; color: #8B87A0; margin-top: 4px; }
.toggle-switch { position: relative; width: 56px; height: 30px; cursor: pointer; }
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-slider { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #D0CDE0; border-radius: 30px; transition: 0.3s; }
.toggle-slider:before { content: ""; position: absolute; height: 24px; width: 24px; left: 3px; bottom: 3px; background: white; border-radius: 50%; transition: 0.3s; box-shadow: 0 2px 4px rgba(0,0,0,0.15); }
input:checked + .toggle-slider { background: linear-gradient(135deg, #7C5CFC, #FF6B35); }
input:checked + .toggle-slider:before { transform: translateX(26px); }
.voice-clone-section { background: linear-gradient(135deg, #F0FFF4, #F0F8FF); border-radius: 16px; padding: 24px; margin-bottom: 24px; border: 1px solid rgba(0,201,167,0.15); }
.voice-clone-section h4 { font-size: 14px; font-weight: 700; color: #00896B; margin-bottom: 12px; }
.voice-clone-section p { font-size: 12px; color: #6B8F80; margin-bottom: 16px; line-height: 1.6; }
.upload-area { border: 2px dashed #B0D8C8; border-radius: 14px; padding: 28px; text-align: center; cursor: pointer; transition: all 0.3s; background: rgba(255,255,255,0.5); }
.upload-area:hover { border-color: #00C9A7; background: rgba(255,255,255,0.8); }
.upload-area.has-file { border-color: #00C9A7; border-style: solid; background: rgba(0,201,167,0.05); }
.upload-icon { font-size: 32px; margin-bottom: 8px; }
.upload-text { font-size: 13px; color: #6B8F80; }
.upload-text strong { color: #00896B; }
.file-info { display: none; margin-top: 12px; padding: 12px 16px; background: rgba(0,201,167,0.08); border-radius: 10px; font-size: 13px; color: #00896B; font-weight: 600; }
.file-info.show { display: flex; align-items: center; justify-content: space-between; }
.file-remove { background: none; border: none; color: #FF6B35; cursor: pointer; font-size: 13px; font-weight: 700; }
input[type="file"] { display: none; }
.gen-btn { width: 100%; padding: 18px; border: none; border-radius: 16px; background: linear-gradient(135deg, #7C5CFC, #9B7AFF); color: white; font-size: 17px; font-weight: 700; cursor: pointer; transition: all 0.3s; font-family: inherit; box-shadow: 0 6px 25px rgba(124,92,252,0.35); }
.gen-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(124,92,252,0.45); }
.gen-btn:disabled { background: #C5C0D8; cursor: not-allowed; box-shadow: none; transform: none; }
.loading { display: none; text-align: center; padding: 40px; }
.loading.show { display: block; }
.spinner { width: 48px; height: 48px; border: 4px solid #E8E6F0; border-top-color: #7C5CFC; border-radius: 50%; animation: spin 0.8s linear infinite; margin: 0 auto 16px; }
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: 14px; color: #8B87A0; }
.result { display: none; margin-top: 24px; }
.result.show { display: block; }
.audio-card { background: linear-gradient(135deg, #F0FFF4, #F0F8FF); border: 2px solid #00C9A7; border-radius: 16px; padding: 24px; }
.audio-card h3 { font-size: 15px; font-weight: 700; color: #00896B; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
audio { width: 100%; margin-top: 8px; border-radius: 8px; }
.meta { font-size: 12px; color: #6B8F80; margin-top: 8px; }
.error { display: none; color: #E53E3E; background: #FFF5F5; padding: 14px 20px; border-radius: 12px; margin-top: 16px; font-size: 14px; }
.error.show { display: block; }
.info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 20px; }
.info-item { background: rgba(255,255,255,0.7); border-radius: 16px; padding: 20px; text-align: center; border: 1px solid rgba(0,0,0,0.04); }
.info-item .icon { font-size: 28px; margin-bottom: 8px; }
.info-item .val { font-size: 22px; font-weight: 800; color: #7C5CFC; }
.info-item .lbl { font-size: 11px; color: #8B87A0; text-transform: uppercase; letter-spacing: 0.5px; margin-top: 4px; }
.arch-box { background: #1E1E2E; color: #CDD6F4; font-family: 'JetBrains Mono', monospace; font-size: 13px; padding: 20px; border-radius: 14px; margin-top: 20px; line-height: 1.8; overflow-x: auto; }
.arch-box .hl { color: #F9E2AF; font-weight: 600; }
.arch-box .ok { color: #A6E3A1; }
.arch-box .dim { color: #6C7086; }
.footer { text-align: center; padding: 32px; font-size: 13px; color: #8B87A0; }
.footer a { color: #7C5CFC; text-decoration: none; font-weight: 600; }
</style>
</head>
<body>
<div class="bg-orbs"><div class="orb orb-1"></div><div class="orb orb-2"></div><div class="orb orb-3"></div></div>
<div class="container">
<div class="header">
<a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="badge-link">
<div class="badge">🧬 FINAL-Bench · World's First Cross-Modal FFN Transfer</div>
</a>
<h1>Darwin-TTS-1.7B-Cross v2</h1>
<p>Emotion-enhanced TTS via LLM→TTS FFN blending. No training required. Now with <strong>Voice Cloning</strong>!</p>
<div class="header-links">
<a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="link-model">πŸ€— Model</a>
<a href="https://huggingface.co/blog/FINAL-Bench/darwin-tts" target="_blank" class="link-blog">πŸ“„ Blog</a>
<a href="https://huggingface.co/spaces/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank" class="link-article">πŸš€ Space</a>
</div>
</div>
<div class="info-grid">
<div class="info-item"><div class="icon">🧬</div><div class="val">84</div><div class="lbl">FFN Tensors</div></div>
<div class="info-item"><div class="icon">βœ…</div><div class="val">100%</div><div class="lbl">Shape Match</div></div>
<div class="info-item"><div class="icon">⚑</div><div class="val">0</div><div class="lbl">Training</div></div>
<div class="info-item"><div class="icon">πŸŽ™οΈ</div><div class="val">Clone</div><div class="lbl">Voice Cloning</div></div>
</div>
<div class="card">
<div class="input-group">
<label>πŸ’¬ Text to Speak</label>
<textarea id="textInput" placeholder="Enter text in Korean, English, or other supported languages...">μ•ˆλ…•ν•˜μ„Έμš”, μ €λŠ” λ‹€μœˆ 인곡지λŠ₯μž…λ‹ˆλ‹€. λ§Œλ‚˜μ„œ λ°˜κ°‘μŠ΅λ‹ˆλ‹€!</textarea>
</div>
<div class="input-group">
<label>πŸ“ Quick Examples</label>
<div class="presets">
<button class="preset-btn" onclick="setPreset('μ•ˆλ…•ν•˜μ„Έμš”, μ €λŠ” λ‹€μœˆ 인곡지λŠ₯μž…λ‹ˆλ‹€.')">πŸ‡°πŸ‡· 인사</button>
<button class="preset-btn" onclick="setPreset('정말 기쁜 μ†Œμ‹μ΄μ—μš”! λ“œλ””μ–΄ ν•΄λƒˆμŠ΅λ‹ˆλ‹€!')">😊 기쁨</button>
<button class="preset-btn" onclick="setPreset('Hello, I am Darwin AI. Nice to meet you!')">πŸ‡ΊπŸ‡Έ English</button>
<button class="preset-btn" onclick="setPreset('였늘 날씨가 정말 μ’‹λ„€μš”. μ‚°μ±…ν•˜κΈ° λ”± 쒋은 λ‚ μ΄μ—μš”.')">🌀️ 일상</button>
</div>
</div>
<div class="toggle-section">
<div class="toggle-row">
<div class="toggle-info">
<h4>🧬 Darwin Enhancement</h4>
<p id="toggleDesc">LLM FFN blended β€” more expressive, emotional speech</p>
</div>
<label class="toggle-switch">
<input type="checkbox" id="darwinToggle" checked onchange="updateToggle()">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="voice-clone-section">
<h4>πŸŽ™οΈ Voice Cloning (Optional)</h4>
<p>Upload 3~15 seconds of clear speech audio. The model will clone this voice.<br>If no audio is uploaded, a default voice will be used.</p>
<div class="upload-area" id="uploadArea" onclick="document.getElementById('refAudio').click()">
<div class="upload-icon" id="uploadIcon">🎀</div>
<div class="upload-text" id="uploadText"><strong>Click to upload</strong> or drag & drop<br>WAV, MP3, FLAC Β· 3~15 seconds recommended</div>
</div>
<input type="file" id="refAudio" accept="audio/*" onchange="handleAudioUpload(this)">
<div class="file-info" id="fileInfo">
<span id="fileName"></span>
<button class="file-remove" onclick="removeAudio(event)">βœ• Remove</button>
</div>
</div>
<button class="gen-btn" id="genBtn" onclick="generate()">πŸ”Š Generate Speech</button>
<div class="loading" id="loading"><div class="spinner"></div><div class="loading-text">🧬 Generating speech...<br>This may take 30~60 seconds on first run.</div></div>
<div class="error" id="error"></div>
<div class="result" id="result">
<div class="audio-card">
<h3>✨ Darwin-TTS Output</h3>
<audio id="audioPlayer" controls></audio>
<div class="meta" id="audioMeta"></div>
</div>
</div>
</div>
<div class="card">
<h2 style="font-size:20px;font-weight:800;margin-bottom:20px;color:#2D2B3D;">πŸ”¬ How It Works</h2>
<div class="arch-box">
<span class="dim">Darwin-TTS v2 Architecture:</span><br>
β”œβ”€β”€ <span class="hl">talker</span> (28L Qwen3 LM) ← <span class="ok">Layer-selective FFN blending (proprietary)</span><br>
β”œβ”€β”€ code_predictor (5L) ← <span class="dim">untouched</span><br>
β”œβ”€β”€ speech_tokenizer ← <span class="dim">untouched</span><br>
└── encoder/decoder ← <span class="dim">untouched</span><br>
<br>
<span class="dim">v2:</span> <span class="ok">Long text stability + quality exceeds original + Voice Cloning</span>
</div>
</div>
<div class="footer">
<p>Built by <a href="https://huggingface.co/FINAL-Bench" target="_blank">VIDRAFT</a> Β· Darwin Evolutionary Merge Framework</p>
<p style="margin-top:6px;">
<a href="https://huggingface.co/FINAL-Bench/Darwin-TTS-1.7B-Cross" target="_blank">πŸ€— Model</a> Β·
<a href="https://huggingface.co/blog/FINAL-Bench/darwin-tts" target="_blank">πŸ“„ Blog</a> Β· Apache 2.0
</p>
</div>
</div>
<script>
let refAudioB64 = null;
function updateToggle() {
const on = document.getElementById('darwinToggle').checked;
document.getElementById('toggleDesc').textContent = on ? 'LLM FFN blended β€” more expressive, emotional speech' : 'Original Qwen3-TTS β€” no enhancement';
}
function setPreset(text) {
document.getElementById('textInput').value = text;
document.querySelectorAll('.preset-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
}
function handleAudioUpload(input) {
const file = input.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
const bytes = new Uint8Array(e.target.result);
let binary = '';
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
refAudioB64 = btoa(binary);
document.getElementById('uploadArea').classList.add('has-file');
document.getElementById('uploadIcon').textContent = 'βœ…';
document.getElementById('uploadText').innerHTML = '<strong>' + file.name + '</strong><br>' + (file.size / 1024).toFixed(0) + ' KB';
document.getElementById('fileName').textContent = '🎀 ' + file.name;
document.getElementById('fileInfo').classList.add('show');
};
reader.readAsArrayBuffer(file);
}
function removeAudio(e) {
e.stopPropagation();
refAudioB64 = null;
document.getElementById('refAudio').value = '';
document.getElementById('uploadArea').classList.remove('has-file');
document.getElementById('uploadIcon').textContent = '🎀';
document.getElementById('uploadText').innerHTML = '<strong>Click to upload</strong> or drag & drop<br>WAV, MP3, FLAC Β· 3~15 seconds recommended';
document.getElementById('fileInfo').classList.remove('show');
}
const ua = document.getElementById('uploadArea');
ua.addEventListener('dragover', e => { e.preventDefault(); ua.style.borderColor = '#00C9A7'; });
ua.addEventListener('dragleave', () => { ua.style.borderColor = ''; });
ua.addEventListener('drop', e => { e.preventDefault(); ua.style.borderColor = ''; if (e.dataTransfer.files.length) { document.getElementById('refAudio').files = e.dataTransfer.files; handleAudioUpload(document.getElementById('refAudio')); }});
async function generate() {
const text = document.getElementById('textInput').value.trim();
const useDarwin = document.getElementById('darwinToggle').checked;
if (!text) return;
const btn = document.getElementById('genBtn');
const loading = document.getElementById('loading');
const result = document.getElementById('result');
const error = document.getElementById('error');
btn.disabled = true; btn.textContent = '⏳ Generating...';
loading.classList.add('show'); result.classList.remove('show'); error.classList.remove('show');
try {
const body = { text, use_darwin: useDarwin };
if (refAudioB64) body.ref_audio = refAudioB64;
const resp = await fetch('/synthesize', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
if (!resp.ok) { const err = await resp.json(); throw new Error(err.detail || 'Generation failed'); }
const blob = await resp.blob();
const url = URL.createObjectURL(blob);
const dur = resp.headers.get('X-Duration') || '?';
const model = resp.headers.get('X-Model') || (useDarwin ? 'Darwin' : 'Original');
document.getElementById('audioPlayer').src = url;
document.getElementById('audioMeta').textContent = 'Duration: ' + dur + 's Β· Model: ' + model + ' Β· Voice: ' + (refAudioB64 ? 'Cloned' : 'Default');
result.classList.add('show');
} catch (e) { error.textContent = '❌ ' + e.message; error.classList.add('show'); }
finally { btn.disabled = false; btn.textContent = 'πŸ”Š Generate Speech'; loading.classList.remove('show'); }
}
</script>
</body>
</html>