Improved audio system with voice detection and test button

This commit is contained in:
Dauren777 2026-06-19 07:05:15 +00:00
parent f1e4776b33
commit 17ea76e786

View File

@ -699,8 +699,12 @@ body {
<div class="container">
<div class="section-header"><div class="badge">ШАГ 1</div><h2>Арабский алфавит</h2><p>28 букв — основа чтения Корана</p></div>
<div style="text-align:center;margin-bottom:24px">
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap">
<button class="btn btn-primary" onclick="playAllLetters()">🔊 Прослушать все буквы</button>
<p style="font-size:13px;color:var(--text-secondary);margin-top:8px">Нажмите на букву или 🔊 чтобы услышать произношение</p>
<button class="btn btn-secondary" onclick="testAudio()">🔊 Тест звука</button>
</div>
<p style="font-size:13px;color:var(--text-secondary);margin-top:12px">Нажмите «Тест звука» чтобы проверить работает ли аудио</p>
<div id="audioStatus" style="margin-top:8px;font-size:12px;padding:8px;border-radius:8px;display:none"></div>
</div>
<div id="alphabetGrid"></div>
</div>
@ -1258,7 +1262,36 @@ function renderAlphabet() {
`).join('');
}
let currentAudio = null;
// Audio System - Multiple fallbacks
let voicesLoaded = false;
let arabicVoice = null;
// Load voices
function loadVoices() {
return new Promise((resolve) => {
const voices = speechSynthesis.getVoices();
if (voices.length > 0) {
arabicVoice = voices.find(v => v.lang.startsWith('ar')) || voices.find(v => v.lang.includes('ar')) || null;
voicesLoaded = true;
resolve();
} else {
speechSynthesis.onvoiceschanged = () => {
const voices = speechSynthesis.getVoices();
arabicVoice = voices.find(v => v.lang.startsWith('ar')) || voices.find(v => v.lang.includes('ar')) || null;
voicesLoaded = true;
resolve();
};
// Timeout fallback
setTimeout(() => {
voicesLoaded = true;
resolve();
}, 2000);
}
});
}
// Initialize voices
loadVoices();
function speakLetter(idx) {
const letter = alphabetData[idx].letter;
@ -1267,50 +1300,66 @@ function speakLetter(idx) {
}
function playArabicAudio(text, label) {
// Stop any currently playing audio
if (currentAudio) {
currentAudio.pause();
currentAudio = null;
}
// Use Google Translate TTS (free, public API)
const encoded = encodeURIComponent(text);
const url = `https://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&q=${encoded}&tl=ar`;
currentAudio = new Audio(url);
currentAudio.crossOrigin = 'anonymous';
currentAudio.onplay = () => {
showToast('🔊 ' + (label || text), 'info');
};
currentAudio.onerror = () => {
// Fallback to Web Speech API
// Try Web Speech API first
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'ar-SA';
utterance.rate = 0.5;
window.speechSynthesis.speak(utterance);
utterance.rate = 0.6;
utterance.pitch = 1;
utterance.volume = 1;
if (arabicVoice) {
utterance.voice = arabicVoice;
}
showToast('🔊 ' + (label || text), 'info');
utterance.onerror = (e) => {
console.error('Speech error:', e);
showToast('Ошибка воспроизведения. Попробуйте другой браузер.', 'error');
};
currentAudio.play().catch(() => {
// Fallback to Web Speech API
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'ar-SA';
utterance.rate = 0.5;
window.speechSynthesis.speak(utterance);
} else {
showToast('Ваш браузер не поддерживает аудио', 'error');
}
showToast('🔊 ' + (label || text), 'info');
});
}
function testAudio() {
const statusEl = document.getElementById('audioStatus');
statusEl.style.display = 'block';
statusEl.style.background = 'var(--bg-card)';
statusEl.style.color = 'var(--text-secondary)';
statusEl.textContent = 'Проверка аудио...';
// Check speech synthesis support
if (!('speechSynthesis' in window)) {
statusEl.style.background = 'rgba(239,68,68,0.1)';
statusEl.style.color = 'var(--error)';
statusEl.textContent = '❌ Ваш браузер не поддерживает синтез речи. Попробуйте Chrome или Edge.';
return;
}
// Check voices
const voices = speechSynthesis.getVoices();
const arVoice = voices.find(v => v.lang.startsWith('ar'));
if (arVoice) {
statusEl.textContent = '✅ Арабский голос найден: ' + arVoice.name + '. Воспроизвожу тест...';
} else if (voices.length > 0) {
statusEl.textContent = '⚠️ Арабский голос не найден. Используется стандартный голос. Голосов доступно: ' + voices.length;
} else {
statusEl.textContent = '⏳ Голоса загружаются... Попробуйте ещё раз через секунду.';
}
playArabicAudio('بسم الله الرحمن الرحيم', 'Тест');
}
function playAllLetters() {
let index = 0;
const cards = document.querySelectorAll('.letter-card');
function playNext() {
if (index < alphabetData.length) {
@ -1318,7 +1367,6 @@ function playAllLetters() {
const name = alphabetData[index].name;
// Highlight current letter
const cards = document.querySelectorAll('.letter-card');
cards.forEach(c => c.style.borderColor = 'var(--border)');
if (cards[index]) {
cards[index].style.borderColor = 'var(--quran-gold)';
@ -1327,29 +1375,30 @@ function playAllLetters() {
showToast('Буква ' + name + ' (' + (index + 1) + '/28)', 'info');
const encoded = encodeURIComponent(letter);
const url = `https://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&q=${encoded}&tl=ar`;
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(letter);
utterance.lang = 'ar-SA';
utterance.rate = 0.6;
if (arabicVoice) utterance.voice = arabicVoice;
const audio = new Audio(url);
audio.crossOrigin = 'anonymous';
audio.onended = () => {
utterance.onend = () => {
index++;
setTimeout(playNext, 500);
};
audio.onerror = () => {
utterance.onerror = () => {
index++;
setTimeout(playNext, 500);
};
audio.play().catch(() => {
window.speechSynthesis.speak(utterance);
} else {
index++;
setTimeout(playNext, 500);
});
setTimeout(playNext, 1000);
}
} else {
showToast('Все буквы прослушаны!', 'success');
const cards = document.querySelectorAll('.letter-card');
cards.forEach(c => c.style.borderColor = 'var(--border)');
}
}
@ -1650,38 +1699,46 @@ function speakFullSurah(name) {
ikhlas: 'قُلْ هُوَ اللَّهُ أَحَدٌ. اللَّهُ الصَّمَدُ. لَمْ يَلِدْ وَلَمْ يُولَدْ. وَلَمْ يَكُن لَّهُ كُفُوًا أَحَدٌ'
};
if (!('speechSynthesis' in window)) {
showToast('Ваш браузер не поддерживает аудио', 'error');
return;
}
const text = surahs[name];
const parts = text.split('. ');
let index = 0;
function playNext() {
function speakNext() {
if (index < parts.length) {
const encoded = encodeURIComponent(parts[index]);
const url = `https://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&q=${encoded}&tl=ar`;
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(parts[index]);
utterance.lang = 'ar-SA';
utterance.rate = 0.6;
utterance.pitch = 1;
utterance.volume = 1;
const audio = new Audio(url);
audio.crossOrigin = 'anonymous';
if (arabicVoice) {
utterance.voice = arabicVoice;
}
audio.onended = () => {
utterance.onend = () => {
index++;
setTimeout(playNext, 400);
setTimeout(speakNext, 400);
};
audio.onerror = () => {
utterance.onerror = () => {
index++;
setTimeout(playNext, 400);
setTimeout(speakNext, 400);
};
audio.play().catch(() => {
index++;
setTimeout(playNext, 400);
});
window.speechSynthesis.speak(utterance);
showToast('Аят ' + (index + 1) + '/' + parts.length, 'info');
} else {
showToast('Сура прослушана!', 'success');
}
}
playNext();
speakNext();
}
function markSurahPracticed(id) {