Upgrade TTS to ElevenLabs Adam voice with frontend integration
- Switch speakText() to call /api/text-to-speech backend (ElevenLabs) with fallback to native speechSynthesis - Upgrade ElevenLabs voice from Rachel to Adam (pNInz6obpgDQGcFmaJgB) — warmer, more professional - Upgrade model from eleven_monolingual_v1 to eleven_turbo_v2_5 for better quality and lower latency
This commit is contained in:
parent
4fc7f7f033
commit
64761c09fd
2 changed files with 34 additions and 10 deletions
|
|
@ -142,17 +142,41 @@ function speakText(elementId) {
|
|||
if (!el) return;
|
||||
var text = (el.innerText || el.textContent).trim();
|
||||
if (!text) { showToast('Nothing to read', 'error'); return; }
|
||||
if (!('speechSynthesis' in window)) { showToast('TTS not supported', 'error'); return; }
|
||||
window.speechSynthesis.cancel();
|
||||
var utter = new SpeechSynthesisUtterance(text);
|
||||
utter.rate = 0.9;
|
||||
utter.onend = function() { stopReading(); };
|
||||
utter.onerror = function() { stopReading(); };
|
||||
|
||||
currentlyReadingId = elementId;
|
||||
var btn = findReadButton(elementId);
|
||||
if (btn) { btn.classList.add('btn-reading'); btn.innerHTML = '<i class="fas fa-stop"></i> Stop'; }
|
||||
window.speechSynthesis.speak(utter);
|
||||
showToast('Reading — click Stop to end', 'info');
|
||||
|
||||
// Try ElevenLabs backend first
|
||||
var token = window.AUTH_TOKEN || localStorage.getItem('ped_scribe_token') || '';
|
||||
fetch('/api/text-to-speech', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
|
||||
body: JSON.stringify({ text: text })
|
||||
})
|
||||
.then(function(r) {
|
||||
if (!r.ok) throw new Error('not configured');
|
||||
return r.blob();
|
||||
})
|
||||
.then(function(blob) {
|
||||
var url = URL.createObjectURL(blob);
|
||||
currentAudio = new Audio(url);
|
||||
currentAudio.onended = function() { URL.revokeObjectURL(url); stopReading(); };
|
||||
currentAudio.onerror = function() { URL.revokeObjectURL(url); stopReading(); };
|
||||
currentAudio.play();
|
||||
showToast('Reading — click Stop to end', 'info');
|
||||
})
|
||||
.catch(function() {
|
||||
// Fallback to native speech synthesis
|
||||
if (!('speechSynthesis' in window)) { stopReading(); showToast('TTS not supported', 'error'); return; }
|
||||
window.speechSynthesis.cancel();
|
||||
var utter = new SpeechSynthesisUtterance(text);
|
||||
utter.rate = 0.9;
|
||||
utter.onend = function() { stopReading(); };
|
||||
utter.onerror = function() { stopReading(); };
|
||||
window.speechSynthesis.speak(utter);
|
||||
showToast('Reading — click Stop to end', 'info');
|
||||
});
|
||||
}
|
||||
|
||||
function stopReading() {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ router.post('/text-to-speech', authMiddleware, async (req, res) => {
|
|||
if (!process.env.ELEVENLABS_API_KEY) return res.status(400).json({ error: 'Not configured' });
|
||||
const response = await axios({
|
||||
method: 'POST',
|
||||
url: 'https://api.elevenlabs.io/v1/text-to-speech/21m00Tcm4TlvDq8ikWAM',
|
||||
url: 'https://api.elevenlabs.io/v1/text-to-speech/pNInz6obpgDQGcFmaJgB',
|
||||
headers: { 'xi-api-key': process.env.ELEVENLABS_API_KEY, 'Content-Type': 'application/json' },
|
||||
data: { text: req.body.text.substring(0, 5000), model_id: 'eleven_monolingual_v1', voice_settings: { stability: 0.5, similarity_boost: 0.75 } },
|
||||
data: { text: req.body.text.substring(0, 5000), model_id: 'eleven_turbo_v2_5', voice_settings: { stability: 0.5, similarity_boost: 0.75 } },
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
res.set('Content-Type', 'audio/mpeg');
|
||||
|
|
|
|||
Loading…
Reference in a new issue