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