Spaces:
Runtime error
Runtime error
File size: 4,002 Bytes
7017726 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | // ---- Build signature waveform ----
const wave = document.getElementById('wave');
if (wave) {
const BAR_COUNT = 24;
for (let i = 0; i < BAR_COUNT; i++) {
const bar = document.createElement('div');
bar.className = 'bar';
bar.style.animationDelay = (i * 0.06) + 's';
wave.appendChild(bar);
}
}
const bars = wave ? wave.querySelectorAll('.bar') : [];
function setWaveActive(active) {
if (!wave) return;
wave.classList.toggle('is-active', active);
if (!active) bars.forEach(b => b.style.height = '');
}
let waveInterval = null;
function startWaveAnimation() {
setWaveActive(true);
waveInterval = setInterval(() => {
bars.forEach(b => b.style.height = (6 + Math.random() * 28) + 'px');
}, 110);
}
function stopWaveAnimation() {
if (waveInterval) clearInterval(waveInterval);
waveInterval = null;
setWaveActive(false);
}
// ---- Voice card selection ----
const voiceCards = document.querySelectorAll('.voice-card');
voiceCards.forEach(card => {
card.addEventListener('click', () => {
voiceCards.forEach(c => c.classList.remove('selected'));
card.classList.add('selected');
});
});
// ---- Sliders ----
const rate = document.getElementById('rate');
const pitch = document.getElementById('pitch');
const rateVal = document.getElementById('rateVal');
const pitchVal = document.getElementById('pitchVal');
function fmt(v, unit) {
const n = Number(v);
return (n >= 0 ? '+' : '') + n + unit;
}
if (rate) rate.addEventListener('input', () => rateVal.textContent = fmt(rate.value, '%'));
if (pitch) pitch.addEventListener('input', () => pitchVal.textContent = fmt(pitch.value, 'Hz'));
// ---- Character counter ----
const MAX_CHARS = 10000;
const text = document.getElementById('text');
const charCount = document.getElementById('charCount');
if (text) {
text.addEventListener('input', () => {
const len = text.value.length;
charCount.textContent = `${len} / ${MAX_CHARS}`;
charCount.classList.toggle('over', len > MAX_CHARS);
});
}
// ---- Generate ----
const generateBtn = document.getElementById('generateBtn');
const status = document.getElementById('status');
const output = document.getElementById('output');
const player = document.getElementById('player');
const downloadLink = document.getElementById('downloadLink');
let currentObjectUrl = null;
if (generateBtn) {
generateBtn.addEventListener('click', async () => {
const value = text.value.trim();
if (!value) {
status.textContent = 'Please enter some text first.';
status.classList.add('error');
return;
}
if (value.length > MAX_CHARS) {
status.textContent = `Text is too long. Maximum is ${MAX_CHARS} characters.`;
status.classList.add('error');
return;
}
const voice = document.querySelector('input[name="voice"]:checked').value;
status.classList.remove('error');
status.textContent = 'Generating audio...';
generateBtn.disabled = true;
startWaveAnimation();
try {
const res = await fetch('/api/tts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: value,
voice: voice,
rate: Number(rate.value),
pitch: Number(pitch.value)
})
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.error || 'Failed to generate audio.');
}
const blob = await res.blob();
if (currentObjectUrl) URL.revokeObjectURL(currentObjectUrl);
currentObjectUrl = URL.createObjectURL(blob);
player.src = currentObjectUrl;
downloadLink.href = currentObjectUrl;
output.classList.add('visible');
status.textContent = 'Done! Your audio is ready below.';
player.play().catch(() => {});
} catch (e) {
status.textContent = e.message;
status.classList.add('error');
} finally {
generateBtn.disabled = false;
stopWaveAnimation();
}
});
}
|