diff --git a/dist/index.html b/dist/index.html
index 3ad13c6..c8f98d5 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -32,7 +32,7 @@ button:disabled { background:#ccc; cursor:not-allowed; }
🇮🇹 Lyngvo тренажёр итальянского произношения · для Google Chrome
- 1. Введите фразу → 2. ▶ Эталон (прослушайте) → 3. 🎙 Записать (произнесите) → 4. 📊 Сравнить
+ 1. Введите фразу → 2. ▶ Эталон (прослушайте) → 3. 🎙 Записать (сделайте короткую паузу перед произнесением) → 4. 📊 Сравнить
@@ -256,12 +256,15 @@ async function transcribe(blob) {
if (!GROQ_API_KEY) return { text: '', error: 'no_key' };
const tStart = performance.now();
+ // Обрезаем 0.2с в начале (щелчок) — декодируем → WAV
+ const trimmedBlob = await trimStartBlob(blob);
+
const form = new FormData();
- form.append('file', blob, 'audio.webm');
+ form.append('file', trimmedBlob, 'audio.wav');
form.append('model', 'whisper-large-v3');
form.append('language', 'it');
- console.log('[HTTP] blob ' + (blob.size/1024).toFixed(1) + 'KB → multipart POST');
+ console.log('[HTTP] blob ' + (trimmedBlob.size/1024).toFixed(1) + 'KB → multipart POST');
try {
const r = await fetch('https://lang.kube5s.ru/openai/v1/audio/transcriptions', {
@@ -392,23 +395,20 @@ function stopRecording() {
if (mediaRecorder && mediaRecorder.state === 'recording') mediaRecorder.stop();
}
-// ---------- Воспроизведение (с обрезкой тишины) ----------
+// ---------- Воспроизведение (с обрезкой 0.2с в начале) ----------
async function playTrimmed(blob) {
- const t = await trimSilence(blob);
const ctx = await getAudioCtx();
- let buf;
- if (t) {
- const offline = new OfflineAudioContext(1, t.data.length, t.sr);
- buf = offline.createBuffer(1, t.data.length, t.sr);
- buf.getChannelData(0).set(t.data);
- } else {
- const ab = await blob.arrayBuffer();
- buf = await ctx.decodeAudioData(ab);
- }
+ const ab = await blob.arrayBuffer();
+ const rawBuf = await ctx.decodeAudioData(ab);
+ const sr = rawBuf.sampleRate;
+ const data = rawBuf.getChannelData(0);
+ const cutSamples = Math.floor(sr * 0.2);
+ const trimmed = data.slice(cutSamples);
+ const buf = ctx.createBuffer(1, trimmed.length, sr);
+ buf.getChannelData(0).set(trimmed);
const src = ctx.createBufferSource();
src.buffer = buf;
const gain = ctx.createGain();
- // Fade-in 10мс — убирает щелчок при старте
gain.gain.setValueAtTime(0, ctx.currentTime);
gain.gain.linearRampToValueAtTime(1, ctx.currentTime + 0.01);
src.connect(gain).connect(ctx.destination);
@@ -608,6 +608,35 @@ async function trimSilence(blob) {
} catch(e) { return null; }
}
+// ---------- WAV кодирование (для отправки в Whisper) ----------
+function encodeWAV(samples, sampleRate) {
+ const buf = new ArrayBuffer(44 + samples.length * 2);
+ const v = new DataView(buf);
+ const w = (o, s) => { for (let i=0;i