// ---------- Глобальные переменные и инициализация ---------- const GROQ_API_KEY = ''; const VERSION = 'v86'; let mediaRecorder, audioChunks = [], userAudioBlob = null, ttsText = ''; let isRecording = false, isAnalyzing = false, micAnalyser = null, micAnimId = null; let recordStartTime = 0, recordDuration = 0; function setStatus(msg) { document.getElementById('status').textContent = msg; } function enable(id, val) { document.getElementById(id).disabled = !val; } // ---------- Инициализация ---------- document.getElementById('ver').textContent = VERSION; renderHistory(); renderRecHistory(); // ---------- Кнопка Запись ---------- document.getElementById('btnRecord').onclick = async function () { const btn = this; if (isRecording) { btn.disabled = true; stopRecording(); return; } try { isRecording = true; btn.textContent = '■ Стоп'; await startRecording(); } catch (err) { let msg = '⛔ '; if (err.name === 'NotAllowedError') msg += 'Доступ к микрофону запрещён. Нажмите на замочек 🔒 в адресной строке → Микрофон → Разрешить.'; else if (err.name === 'NotFoundError') msg += 'Микрофон не найден. Подключите микрофон.'; else if (err.name === 'NotReadableError') msg += 'Микрофон занят другим приложением.'; else msg += 'Ошибка: ' + (err.message || err.name); setStatus(msg); btn.textContent = '🎙 Записать'; isRecording = false; } }; // ---------- Воспроизведение своей записи ---------- document.getElementById('btnPlayUser').onclick = async () => { if (!userAudioBlob) return setStatus('Нет записи!'); setStatus('🔊 Ваша запись...'); try { await playTrimmed(userAudioBlob); setStatus('Готово.'); } catch (err) { setStatus('Ошибка воспроизведения.'); } }; // ---------- Кнопки ---------- document.getElementById('btnTTS').onclick = async () => { ttsText = document.getElementById('inputText').value.trim(); if (!ttsText) return setStatus('Введите текст!'); saveToHistory(ttsText); translateToRussian(ttsText); setStatus('🔊 Эталон...'); await playTTS(ttsText); setStatus('Готово.'); }; document.getElementById('btnCompare').onclick = async () => { if (!userAudioBlob) return setStatus('Нет записи!'); const original = document.getElementById('inputText').value.trim(); await doCompare(userAudioBlob, original, recordDuration); }; document.getElementById('btnStereo').onclick = async () => { const text = document.getElementById('inputText').value.trim(); if (!text) return setStatus('Введите текст для эталона!'); if (!userAudioBlob) return setStatus('Нет записи! Нажмите 🎙 Записать.'); await playStereo(text, userAudioBlob); }; // ---------- История: compareRec / stereoRec ---------- async function compareRec(id) { try { const db = await openRecDB(); const tx = db.transaction('recs', 'readonly'); const store = tx.objectStore('recs'); const rec = await new Promise(r => { const req = store.get(id); req.onsuccess = () => r(req.result); }); if (rec?.blob) { document.getElementById('inputText').value = rec.word; const tr = await doCompare(rec.blob, rec.word, rec.duration); if (tr) saveTranscription(id, tr); } } catch(e) { setStatus('Ошибка загрузки записи.'); } } async function stereoRec(id) { try { const db = await openRecDB(); const tx = db.transaction('recs', 'readonly'); const store = tx.objectStore('recs'); const rec = await new Promise(r => { const req = store.get(id); req.onsuccess = () => r(req.result); }); if (rec?.blob) { document.getElementById('inputText').value = rec.word; await playStereo(rec.word, rec.blob); } } catch(e) { setStatus('Ошибка загрузки записи.'); } }