// ---- GATE 2: Audio Recording ---- async function startRecording() { if (isRecording) { // Stop recording if (mediaRecorder && mediaRecorder.state === 'recording') { mediaRecorder.stop(); } isRecording = false; gate2Btn.classList.remove('recording'); gate2Btn.textContent = '🔄 Repeat Last 30s'; return; } try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorder = new MediaRecorder(stream); audioChunks = []; mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { audioChunks.push(event.data); } }; mediaRecorder.onstop = () => { audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); // Keep only the last 30 seconds // For demo, we process immediately processAudioBuffer(); }; mediaRecorder.start(1000); // Record in 1-second chunks isRecording = true; gate2Btn.classList.add('recording'); gate2Btn.textContent = '⏹️ Stop Recording'; } catch (err) { alert('Could not access microphone. Please allow microphone access.'); console.error(err); } } // ---- GATE 2: Process Audio Buffer ---- function processAudioBuffer() { if (!audioBlob) { resultDiv.innerHTML = `
⚠️ No audio recorded. Tap Repeat Last 30s first.
`; return; } // Simulate transcription + translation (since we can't run STT locally) // In production, you'd send audioBlob to your STT service const sampleTranscripts = [ "Chef said: deglaze the pan with white wine, then add the stock.", "Now we julienne the carrots and sauté them in butter.", "Blanch the green beans for two minutes, then shock them in ice water.", "Fold the cream into the batter gently.", "Sear the steak on high heat for two minutes each side." ]; // Pick a random transcript for demo const transcript = sampleTranscripts[Math.floor(Math.random() * sampleTranscripts.length)]; // Find culinary terms in the transcript and replace with Malay let translated = transcript; let foundTerms = []; glossary.forEach(term => { const regex = new RegExp(term.en_term, 'gi'); if (transcript.match(regex)) { foundTerms.push(term); translated = translated.replace(regex, `${term.en_term} (${term.malay_term})`); } }); // Build the result display let termsHtml = ''; if (foundTerms.length > 0) { termsHtml = foundTerms.map(t => `
${t.en_term} → 🇲🇾 ${t.malay_term}
` ).join(''); } else { termsHtml = '
No culinary terms detected in this snippet.
'; } resultDiv.innerHTML = `
🎙️ Last 30 Seconds
📝 Original:
“${transcript}”
🇲🇾 Translated:
“${translated}”
🔍 Terms Found:
${termsHtml}
👆 Tap Listen to hear the translation
`; // Auto-speak the translation in Malay const malayText = translated.replace(/\([^)]*\)/g, '').trim(); speakText(malayText, 'ms-MY'); // Reset recording state isRecording = false; gate2Btn.classList.remove('recording'); gate2Btn.textContent = '🔄 Repeat Last 30s'; audioBlob = null; } // ---- GATE 2: Stop Everything ---- function stopAll() { // Stop recording if (mediaRecorder && mediaRecorder.state === 'recording') { mediaRecorder.stop(); } isRecording = false; gate2Btn.classList.remove('recording'); gate2Btn.textContent = '🔄 Repeat Last 30s'; // Cancel speech if (isSpeaking) { window.speechSynthesis.cancel(); isSpeaking = false; listenBtn.textContent = '🔊 Listen'; listenBtn.classList.remove('speaking'); } // Stop STT if (recognition) { recognition.stop(); recognition = null; askBtn.classList.remove('listening'); askBtn.textContent = '🎤 Ask'; } // Clear result resultDiv.innerHTML = `
⏹️ Stopped. Tap Repeat Last 30s to try again.
`; }