Added ResponsiveVoice.js for Arabic audio pronunciation
This commit is contained in:
parent
17ea76e786
commit
947fc39a17
130
index.html
130
index.html
@ -6,6 +6,7 @@
|
||||
<title>QuranReader — Читай Коран на арабском</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Noto+Naskh+Arabic:wght@400;600;700&family=Amiri:wght@400;700&display=swap" rel="stylesheet">
|
||||
<script src="https://code.responsivevoice.org/responsivevoice.js?key=FREE"></script>
|
||||
<style>
|
||||
[data-theme="light"] {
|
||||
--ink: #0F1218; --bg: #FFFFFF; --bg-card: #F2F4F7; --bg-card-hover: #E8FCFF;
|
||||
@ -1262,31 +1263,83 @@ function renderAlphabet() {
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Audio System - Multiple fallbacks
|
||||
let voicesLoaded = false;
|
||||
let arabicVoice = null;
|
||||
// Audio System using ResponsiveVoice
|
||||
function speakLetter(idx) {
|
||||
const letter = alphabetData[idx].letter;
|
||||
const name = alphabetData[idx].name;
|
||||
playArabicAudio(letter, name);
|
||||
}
|
||||
|
||||
// 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();
|
||||
function playArabicAudio(text, label) {
|
||||
showToast('🔊 ' + (label || text), 'info');
|
||||
|
||||
if (typeof responsiveVoice !== 'undefined' && responsiveVoice.voiceSupport()) {
|
||||
responsiveVoice.speak(text, "Arabic Male", {
|
||||
rate: 0.8,
|
||||
pitch: 1,
|
||||
volume: 1
|
||||
});
|
||||
} 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 = 'Проверка аудио...';
|
||||
|
||||
if (typeof responsiveVoice !== 'undefined' && responsiveVoice.voiceSupport()) {
|
||||
statusEl.style.background = 'rgba(34,197,94,0.1)';
|
||||
statusEl.style.color = 'var(--success)';
|
||||
statusEl.textContent = '✅ Аудио поддерживается! Воспроизвожу тест...';
|
||||
responsiveVoice.speak("بسم الله", "Arabic Male", {rate: 0.8});
|
||||
} else {
|
||||
statusEl.style.background = 'rgba(239,68,68,0.1)';
|
||||
statusEl.style.color = 'var(--error)';
|
||||
statusEl.textContent = '❌ Аудио не загружено. Обновите страницу и попробуйте снова.';
|
||||
}
|
||||
}
|
||||
|
||||
function playAllLetters() {
|
||||
let index = 0;
|
||||
const cards = document.querySelectorAll('.letter-card');
|
||||
|
||||
function playNext() {
|
||||
if (index < alphabetData.length) {
|
||||
const letter = alphabetData[index].letter;
|
||||
const name = alphabetData[index].name;
|
||||
|
||||
cards.forEach(c => c.style.borderColor = 'var(--border)');
|
||||
if (cards[index]) {
|
||||
cards[index].style.borderColor = 'var(--quran-gold)';
|
||||
cards[index].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
showToast('Буква ' + name + ' (' + (index + 1) + '/28)', 'info');
|
||||
|
||||
if (typeof responsiveVoice !== 'undefined' && responsiveVoice.voiceSupport()) {
|
||||
responsiveVoice.speak(letter, "Arabic Male", {
|
||||
rate: 0.8,
|
||||
onend: () => {
|
||||
index++;
|
||||
setTimeout(playNext, 500);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
index++;
|
||||
setTimeout(playNext, 1000);
|
||||
}
|
||||
} 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);
|
||||
showToast('Все буквы прослушаны!', 'success');
|
||||
cards.forEach(c => c.style.borderColor = 'var(--border)');
|
||||
}
|
||||
}
|
||||
|
||||
playNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1699,8 +1752,8 @@ function speakFullSurah(name) {
|
||||
ikhlas: 'قُلْ هُوَ اللَّهُ أَحَدٌ. اللَّهُ الصَّمَدُ. لَمْ يَلِدْ وَلَمْ يُولَدْ. وَلَمْ يَكُن لَّهُ كُفُوًا أَحَدٌ'
|
||||
};
|
||||
|
||||
if (!('speechSynthesis' in window)) {
|
||||
showToast('Ваш браузер не поддерживает аудио', 'error');
|
||||
if (typeof responsiveVoice === 'undefined' || !responsiveVoice.voiceSupport()) {
|
||||
showToast('Аудио не поддерживается', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1710,29 +1763,14 @@ function speakFullSurah(name) {
|
||||
|
||||
function speakNext() {
|
||||
if (index < parts.length) {
|
||||
window.speechSynthesis.cancel();
|
||||
const utterance = new SpeechSynthesisUtterance(parts[index]);
|
||||
utterance.lang = 'ar-SA';
|
||||
utterance.rate = 0.6;
|
||||
utterance.pitch = 1;
|
||||
utterance.volume = 1;
|
||||
|
||||
if (arabicVoice) {
|
||||
utterance.voice = arabicVoice;
|
||||
}
|
||||
|
||||
utterance.onend = () => {
|
||||
index++;
|
||||
setTimeout(speakNext, 400);
|
||||
};
|
||||
|
||||
utterance.onerror = () => {
|
||||
index++;
|
||||
setTimeout(speakNext, 400);
|
||||
};
|
||||
|
||||
window.speechSynthesis.speak(utterance);
|
||||
showToast('Аят ' + (index + 1) + '/' + parts.length, 'info');
|
||||
responsiveVoice.speak(parts[index], "Arabic Male", {
|
||||
rate: 0.8,
|
||||
onend: () => {
|
||||
index++;
|
||||
setTimeout(speakNext, 400);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showToast('Сура прослушана!', 'success');
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user