正在录音...
学伴正在思考中...
你讲的话
00:00 / 00:00
有权限问题的话,请点击这里
// 模型配置 - 使用与Sample.html相同的CDN链接 const models = { shizuku: { jsonPath: "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json", name: "小智" }, koharu: { jsonPath: "https://unpkg.com/live2d-widget-model-koharu@1.0.5/assets/koharu.model.json", name: "小荷" }, wanko: { jsonPath: "https://unpkg.com/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json", name: "汪喵" } }; // 录音相关变量 let mediaRecorder; let audioChunks = []; let isRecording = false; // 音频播放相关变量 let audioElement = null; let isPlaying = false; // 获取URL参数 function getUrlParam(name) { const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); const r = window.location.search.substr(1).match(reg); return r ? unescape(r[2]) : null; } // 开始录音 function startRecording() { if (isRecording) return; console.log("尝试开始录音"); navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { mediaRecorder = new MediaRecorder(stream); audioChunks = []; mediaRecorder.ondataavailable = event => { if (event.data.size > 0) audioChunks.push(event.data); }; mediaRecorder.onstop = () => { const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); console.log("录音完成,音频数据大小:", audioBlob.size); const audioUrl = URL.createObjectURL(audioBlob); console.log("录音URL:", audioUrl); // 这里可以调用ASR服务 uploadAudioToServer(audioBlob); }; mediaRecorder.start(); isRecording = true; document.getElementById('recordingIndicator').style.display = 'flex'; document.getElementById('startRecordBtn').style.display = 'none'; document.getElementById('stopRecordBtn').style.display = 'flex'; console.log("开始录音成功"); // 设置最长录音时间为60秒 setTimeout(stopRecording, 60000); }) .catch(error => { console.error("获取麦克风权限失败:", error); alert("请授权麦克风权限以使用录音功能"); }); } // 停止录音 function stopRecording() { if (!isRecording || !mediaRecorder) return; mediaRecorder.stop(); isRecording = false; document.getElementById('recordingIndicator').style.display = 'none'; document.getElementById('startRecordBtn').style.display = 'flex'; document.getElementById('stopRecordBtn').style.display = 'none'; console.log("停止录音"); if (mediaRecorder.stream) { mediaRecorder.stream.getTracks().forEach(track => track.stop()); } } // 上传音频到服务器 function uploadAudioToServer(audioBlob) { console.log("开始上传音频到服务器"); // 显示思考中动画 document.getElementById('thinkingIndicator').style.display = 'flex'; const formData = new FormData(); formData.append('file', audioBlob, 'recording.wav'); fetch('/api/xueban/upload-audio', { method: 'POST', body: formData }) .then(response => { if (!response.ok) { throw new Error('服务器响应错误'); } return response.json(); }) .then(data => { console.log("处理结果:", data); // 隐藏思考中动画 document.getElementById('thinkingIndicator').style.display = 'none'; if (data.success) { showResults(data.data); } else { alert('音频处理失败: ' + data.message); } }) .catch(error => { console.error("上传音频失败:", error); // 隐藏思考中动画 document.getElementById('thinkingIndicator').style.display = 'none'; alert('上传音频失败: ' + error.message); }); } // 显示ASR识别结果和反馈 function showResults(data) { // 更新结果显示容器 const resultContainer = document.getElementById('resultContainer'); resultContainer.style.display = 'flex'; // 显示ASR结果 document.getElementById('asrResultText').textContent = data.asr_text || '未识别到内容'; // 显示反馈文本 document.getElementById('feedbackResultText').textContent = data.feedback_text || '无反馈内容'; // 准备音频播放 if (data.audio_url) { if (audioElement) { audioElement.pause(); audioElement = null; } audioElement = new Audio(data.audio_url); audioElement.onloadedmetadata = function() { updateAudioTimeDisplay(); // 音频加载完成后自动播放 try { audioElement.play(); isPlaying = true; updatePlayButton(); } catch (e) { console.error("自动播放失败:", e); } // 无论自动播放是否成功,都显示播放按钮 document.getElementById('playAudioBtn').style.display = 'flex'; }; audioElement.ontimeupdate = function() { updateAudioProgress(); updateAudioTimeDisplay(); }; audioElement.onended = function() { isPlaying = false; updatePlayButton(); }; // 绑定播放按钮事件 document.getElementById('playAudioBtn').onclick = togglePlayAudio; // 绑定进度条点击事件 document.getElementById('audioProgress').onclick = function(e) { if (!audioElement) return; const progressBar = document.getElementById('audioProgress'); const rect = progressBar.getBoundingClientRect(); const clickPosition = (e.clientX - rect.left) / rect.width; audioElement.currentTime = clickPosition * audioElement.duration; }; } } // 切换音频播放/暂停 function togglePlayAudio() { if (!audioElement) return; if (isPlaying) { audioElement.pause(); } else { audioElement.play(); } isPlaying = !isPlaying; updatePlayButton(); } // 更新播放按钮状态 function updatePlayButton() { const playButton = document.getElementById('playAudioBtn'); if (isPlaying) { playButton.innerHTML = ` `; } else { playButton.innerHTML = ` `; } } // 更新音频进度条 function updateAudioProgress() { if (!audioElement || !audioElement.duration) return; const progress = (audioElement.currentTime / audioElement.duration) * 100; document.getElementById('progressBar').style.width = `${progress}%`; } // 更新音频时间显示 function updateAudioTimeDisplay() { if (!audioElement || !audioElement.duration) return; const currentTime = formatTime(audioElement.currentTime); const duration = formatTime(audioElement.duration); document.getElementById('audioTime').textContent = `${currentTime} / ${duration}`; } // 格式化时间为 MM:SS function formatTime(seconds) { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; } // 初始化看板娘 - 简化为Sample.html的工作版本 function initL2Dwidget() { const modelId = getUrlParam('id') || 'koharu'; const model = models[modelId] || models.koharu; document.getElementById('model-select').value = modelId; console.log('加载模型:', model.name, model.jsonPath); // 初始化模型 - 与Sample.html相同的配置 L2Dwidget.init({ "model": { "jsonPath": model.jsonPath, "scale": 1 }, "display": { "position": "right", "width": 150, "height": 300, "hOffset": 0, // 重置水平偏移 "vOffset": -20 // 重置垂直偏移 }, "mobile": { "show": true, "scale": 0.5 }, "react": { "opacityDefault": 0.8, "opacityOnHover": 1 }, "dialog": { "enable": true, "script": { 'tap body': `你好啊,我是${model.name}。`, 'tap face': '有什么问题或者烦心事都可以和我聊聊~' }} }); } // 页面加载完成后初始化 window.onload = function() { // 直接初始化看板娘,不添加额外延迟 initL2Dwidget(); // 监听下拉框变化(使用独立JS暴露的接口) document.getElementById('model-select').addEventListener('change', function() { window.switchL2DModel(this.value); }); // 绑定录音按钮事件 document.getElementById('startRecordBtn').addEventListener('click', startRecording); document.getElementById('stopRecordBtn').addEventListener('click', stopRecording); // 页面加载时请求麦克风权限 navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { console.log("麦克风权限已授予"); // 立即停止流,只获取权限 stream.getTracks().forEach(track => track.stop()); }) .catch(error => { console.error("获取麦克风权限失败:", error); alert("请授权麦克风权限以使用录音功能"); }); };