741 lines
28 KiB
JavaScript
741 lines
28 KiB
JavaScript
/**
|
||
* 学伴录音功能核心逻辑
|
||
* 模块化组织:录音管理、ASR处理、音频播放、UI控制
|
||
*/
|
||
|
||
// ==================== 全局状态管理 ====================
|
||
const AudioState = {
|
||
recording: {
|
||
mediaRecorder: null,
|
||
audioChunks: [],
|
||
isRecording: false,
|
||
maxDuration: 60000 // 60秒
|
||
},
|
||
playback: {
|
||
audioElement: null,
|
||
isPlaying: false,
|
||
audioChunks: [], // 存储接收到的音频块
|
||
audioQueue: [], // 音频队列,用于流式播放
|
||
isStreamPlaying: false, // 是否正在流式播放
|
||
currentAudioIndex: 0 // 当前播放的音频索引
|
||
},
|
||
websocket: {
|
||
connection: null,
|
||
isConnected: false
|
||
}
|
||
};
|
||
|
||
// ==================== 工具函数 ====================
|
||
const Utils = {
|
||
|
||
// 格式化时间显示
|
||
formatTime(seconds) {
|
||
const mins = Math.floor(seconds / 60).toString().padStart(2, '0');
|
||
const secs = Math.floor(seconds % 60).toString().padStart(2, '0');
|
||
return `${mins}:${secs}`;
|
||
},
|
||
|
||
// 将Blob转换为Base64
|
||
blobToBase64(blob) {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onloadend = () => resolve(reader.result.split(',')[1]);
|
||
reader.onerror = reject;
|
||
reader.readAsDataURL(blob);
|
||
});
|
||
}
|
||
};
|
||
|
||
// ==================== UI控制器 ====================
|
||
const UIController = {
|
||
// 显示/隐藏元素
|
||
toggleElement(elementId, show) {
|
||
const element = document.getElementById(elementId);
|
||
if (element) {
|
||
element.style.display = show ? 'flex' : 'none';
|
||
}
|
||
},
|
||
|
||
// 更新按钮状态
|
||
updateRecordingButtons(isRecording) {
|
||
this.toggleElement('recordingIndicator', isRecording);
|
||
this.toggleElement('startRecordBtn', !isRecording);
|
||
this.toggleElement('stopRecordBtn', isRecording);
|
||
},
|
||
|
||
// 禁用/启用帮我讲题按钮
|
||
setStartRecordButtonEnabled(enabled) {
|
||
const startBtn = document.getElementById('startRecordBtn');
|
||
if (startBtn) {
|
||
startBtn.disabled = !enabled;
|
||
startBtn.style.opacity = enabled ? '1' : '0.5';
|
||
startBtn.style.cursor = enabled ? 'pointer' : 'not-allowed';
|
||
}
|
||
},
|
||
|
||
// 更新播放按钮图标
|
||
updatePlayButton(isPlaying) {
|
||
const btn = document.getElementById('playAudioBtn');
|
||
if (!btn) return;
|
||
|
||
const playIcon = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M8 5V19L19 12L8 5Z" fill="white"/></svg>';
|
||
const pauseIcon = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M6 19H10V5H6V19ZM14 19H18V5H14V19Z" fill="white"/></svg>';
|
||
|
||
btn.innerHTML = isPlaying ? pauseIcon : playIcon;
|
||
},
|
||
|
||
// 更新进度条
|
||
updateProgress(progress) {
|
||
const progressBar = document.getElementById('progressBar');
|
||
if (progressBar) {
|
||
progressBar.style.width = `${progress}%`;
|
||
}
|
||
},
|
||
|
||
// 更新时间显示
|
||
updateTimeDisplay(currentTime, duration) {
|
||
const timeDisplay = document.getElementById('audioTime');
|
||
if (timeDisplay) {
|
||
timeDisplay.textContent = `${Utils.formatTime(currentTime)} / ${Utils.formatTime(duration)}`;
|
||
}
|
||
}
|
||
};
|
||
|
||
// ==================== WebSocket管理模块 ====================
|
||
const WebSocketManager = {
|
||
// 初始化WebSocket连接
|
||
initConnection() {
|
||
console.log('初始化WebSocket连接');
|
||
if (AudioState.websocket.connection &&
|
||
AudioState.websocket.connection.readyState === WebSocket.OPEN) {
|
||
console.log('WebSocket连接已存在');
|
||
return;
|
||
}
|
||
|
||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||
const wsUrl = `${protocol}//${window.location.host}/api/xueban/streaming-chat`;
|
||
|
||
console.log('正在建立WebSocket连接:', wsUrl);
|
||
AudioState.websocket.connection = new WebSocket(wsUrl);
|
||
|
||
// 连接打开
|
||
AudioState.websocket.connection.onopen = () => {
|
||
console.log('WebSocket连接已建立');
|
||
AudioState.websocket.isConnected = true;
|
||
};
|
||
|
||
// 连接关闭
|
||
AudioState.websocket.connection.onclose = () => {
|
||
console.log('WebSocket连接已关闭');
|
||
AudioState.websocket.isConnected = false;
|
||
};
|
||
|
||
// 连接错误
|
||
AudioState.websocket.connection.onerror = (error) => {
|
||
console.error('WebSocket连接错误:', error);
|
||
AudioState.websocket.isConnected = false;
|
||
UIController.toggleElement('thinkingIndicator', false);
|
||
UIController.setStartRecordButtonEnabled(true);
|
||
alert('连接服务器失败,请稍后再试');
|
||
};
|
||
|
||
// 接收消息
|
||
AudioState.websocket.connection.onmessage = (event) => {
|
||
console.log('收到WebSocket消息:', {
|
||
type: typeof event.data,
|
||
size: typeof event.data === 'string' ? event.data.length : event.data.size
|
||
});
|
||
this.handleMessage(event);
|
||
};
|
||
},
|
||
|
||
// 处理接收到的消息
|
||
async handleMessage(event) {
|
||
// 检查消息类型
|
||
if (typeof event.data === 'string') {
|
||
// JSON消息
|
||
try {
|
||
const data = JSON.parse(event.data);
|
||
console.log('解析JSON消息成功:', data);
|
||
|
||
switch (data.type) {
|
||
case 'asr_result':
|
||
// 显示ASR识别结果
|
||
console.log('收到ASR结果:', data.text);
|
||
const asrTextElement = document.getElementById('asrResultText');
|
||
if (asrTextElement) {
|
||
asrTextElement.textContent = data.text || '未识别到内容';
|
||
}
|
||
break;
|
||
|
||
case 'end':
|
||
// 处理结束
|
||
console.log('流式处理完成');
|
||
console.log('当前音频块数量:', AudioState.playback.audioChunks.length);
|
||
UIController.toggleElement('thinkingIndicator', false);
|
||
UIController.setStartRecordButtonEnabled(true);
|
||
|
||
// 标记流式播放结束
|
||
AudioState.playback.isStreamPlaying = false;
|
||
|
||
// 如果有音频数据但尚未开始播放,则开始播放
|
||
if (AudioState.playback.audioQueue.length > 0 && !AudioState.playback.isPlaying) {
|
||
console.log('开始播放队列中的音频');
|
||
AudioPlayer.processAudioQueue();
|
||
}
|
||
break;
|
||
|
||
case 'error':
|
||
// 错误处理
|
||
console.error('收到错误消息:', data.message);
|
||
UIController.toggleElement('thinkingIndicator', false);
|
||
UIController.setStartRecordButtonEnabled(true);
|
||
// 重置流式播放状态
|
||
AudioState.playback.isStreamPlaying = false;
|
||
AudioState.playback.audioQueue = [];
|
||
alert('处理失败: ' + data.message);
|
||
break;
|
||
|
||
default:
|
||
console.log('未知消息类型:', data.type);
|
||
}
|
||
} catch (e) {
|
||
console.error('解析JSON消息失败:', e);
|
||
console.error('原始消息内容:', event.data);
|
||
}
|
||
} else {
|
||
// 修改WebSocketManager.handleMessage方法(约第210-225行)
|
||
// 二进制音频数据
|
||
console.log('收到音频数据,大小:', event.data.size);
|
||
console.log('音频数据类型:', event.data.type);
|
||
|
||
// 保存到原始音频块数组
|
||
AudioState.playback.audioChunks.push(event.data);
|
||
|
||
// 添加到音频队列(用于流式播放)
|
||
AudioState.playback.audioQueue.push(event.data);
|
||
console.log('当前音频队列长度:', AudioState.playback.audioQueue.length);
|
||
|
||
// 显示播放界面
|
||
UIController.toggleElement('resultContainer', true);
|
||
|
||
// 关键修复:立即开始处理音频队列,实现流式播放
|
||
if (!AudioState.playback.isStreamPlaying) {
|
||
AudioState.playback.isStreamPlaying = true;
|
||
AudioPlayer.processAudioQueue();
|
||
}
|
||
|
||
return;
|
||
}
|
||
},
|
||
|
||
// 合并所有音频块并播放
|
||
combineAndPlayAudio() {
|
||
try {
|
||
console.log('开始合并音频块,数量:', AudioState.playback.audioChunks.length);
|
||
|
||
// 创建一个新的Blob,包含所有音频块
|
||
const combinedBlob = new Blob(AudioState.playback.audioChunks, { type: 'audio/wav' });
|
||
console.log('合并后的Blob大小:', combinedBlob.size);
|
||
|
||
// 创建音频URL
|
||
const audioUrl = URL.createObjectURL(combinedBlob);
|
||
console.log('创建音频URL:', audioUrl);
|
||
|
||
// 初始化音频播放器
|
||
AudioPlayer.initPlayer(audioUrl);
|
||
|
||
} catch (error) {
|
||
console.error('合并和播放音频失败:', error);
|
||
}
|
||
},
|
||
|
||
// 关闭WebSocket连接
|
||
closeConnection() {
|
||
if (AudioState.websocket.connection) {
|
||
AudioState.websocket.connection.close();
|
||
AudioState.websocket.connection = null;
|
||
AudioState.websocket.isConnected = false;
|
||
console.log('WebSocket连接已关闭');
|
||
}
|
||
}
|
||
};
|
||
|
||
// ==================== 音频播放模块 ====================
|
||
const AudioPlayer = {
|
||
// 初始化音频播放器
|
||
initPlayer(audioUrl) {
|
||
console.log('AudioPlayer.initPlayer 被调用,音频URL:', audioUrl);
|
||
|
||
// 停止当前播放的音频
|
||
if (AudioState.playback.audioElement) {
|
||
console.log('停止当前播放的音频');
|
||
AudioState.playback.audioElement.pause();
|
||
}
|
||
|
||
// 创建新的音频元素
|
||
console.log('创建新的音频元素');
|
||
AudioState.playback.audioElement = new Audio(audioUrl);
|
||
AudioState.playback.isPlaying = false;
|
||
|
||
// 绑定音频事件
|
||
AudioState.playback.audioElement.onloadedmetadata = () => {
|
||
console.log('音频元数据加载完成');
|
||
this.updateTimeDisplay();
|
||
this.play(); // 自动播放
|
||
};
|
||
|
||
AudioState.playback.audioElement.onplay = () => {
|
||
console.log('音频开始播放');
|
||
};
|
||
|
||
AudioState.playback.audioElement.onpause = () => {
|
||
console.log('音频暂停');
|
||
};
|
||
|
||
AudioState.playback.audioElement.ontimeupdate = () => {
|
||
this.updateProgress();
|
||
this.updateTimeDisplay();
|
||
};
|
||
|
||
AudioState.playback.audioElement.onended = () => {
|
||
console.log('音频播放结束');
|
||
AudioState.playback.isPlaying = false;
|
||
UIController.updatePlayButton(false);
|
||
};
|
||
|
||
AudioState.playback.audioElement.onerror = (error) => {
|
||
console.error('音频播放错误:', error);
|
||
};
|
||
|
||
// 绑定播放按钮点击事件
|
||
const playBtn = document.getElementById('playAudioBtn');
|
||
if (playBtn) {
|
||
playBtn.onclick = () => this.togglePlay();
|
||
}
|
||
|
||
// 绑定进度条点击事件
|
||
const progressContainer = document.getElementById('audioProgress');
|
||
if (progressContainer) {
|
||
progressContainer.onclick = (e) => {
|
||
const rect = progressContainer.getBoundingClientRect();
|
||
const clickPosition = (e.clientX - rect.left) / rect.width;
|
||
AudioState.playback.audioElement.currentTime = clickPosition * AudioState.playback.audioElement.duration;
|
||
};
|
||
}
|
||
},
|
||
|
||
// 播放/暂停切换
|
||
// 修改AudioPlayer.togglePlay方法
|
||
// 播放/暂停切换
|
||
togglePlay() {
|
||
// 首先检查是否在流式播放
|
||
if (AudioState.playback.isStreamPlaying && AudioState.playback.streamAudioElement) {
|
||
// 流式播放模式
|
||
if (AudioState.playback.streamAudioElement.paused) {
|
||
// 如果当前是暂停状态,播放
|
||
AudioState.playback.streamAudioElement.play();
|
||
UIController.updatePlayButton(true);
|
||
} else {
|
||
// 如果当前正在播放,暂停
|
||
AudioState.playback.streamAudioElement.pause();
|
||
UIController.updatePlayButton(false);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 常规播放模式
|
||
if (!AudioState.playback.audioElement) return;
|
||
|
||
if (AudioState.playback.isPlaying) {
|
||
this.pause();
|
||
} else {
|
||
this.play();
|
||
}
|
||
},
|
||
// 播放
|
||
play() {
|
||
if (!AudioState.playback.audioElement) return;
|
||
|
||
try {
|
||
AudioState.playback.audioElement.play();
|
||
AudioState.playback.isPlaying = true;
|
||
UIController.updatePlayButton(true);
|
||
} catch (e) {
|
||
console.error('播放失败:', e);
|
||
}
|
||
},
|
||
|
||
// 暂停
|
||
pause() {
|
||
if (!AudioState.playback.audioElement) return;
|
||
|
||
AudioState.playback.audioElement.pause();
|
||
AudioState.playback.isPlaying = false;
|
||
UIController.updatePlayButton(false);
|
||
},
|
||
|
||
// 更新进度条
|
||
updateProgress() {
|
||
if (!AudioState.playback.audioElement) return;
|
||
|
||
const progress = (AudioState.playback.audioElement.currentTime / AudioState.playback.audioElement.duration) * 100;
|
||
UIController.updateProgress(progress);
|
||
},
|
||
|
||
// 更新时间显示
|
||
updateTimeDisplay() {
|
||
if (!AudioState.playback.audioElement) return;
|
||
|
||
const currentTime = AudioState.playback.audioElement.currentTime;
|
||
const duration = AudioState.playback.audioElement.duration;
|
||
UIController.updateTimeDisplay(currentTime, duration);
|
||
},
|
||
|
||
// 在AudioPlayer.initStreamPlayer方法中添加暂停事件监听
|
||
// 初始化流式播放器
|
||
initStreamPlayer() {
|
||
// 创建新的音频元素用于流式播放
|
||
if (!AudioState.playback.streamAudioElement) {
|
||
AudioState.playback.streamAudioElement = new Audio();
|
||
|
||
// 监听音频结束事件
|
||
AudioState.playback.streamAudioElement.addEventListener('ended', () => {
|
||
// 当前音频播放完毕,处理队列中的下一个音频
|
||
UIController.updatePlayButton(false); // 更新按钮状态
|
||
this.processAudioQueue();
|
||
});
|
||
|
||
// 监听暂停事件
|
||
AudioState.playback.streamAudioElement.addEventListener('pause', () => {
|
||
// 当音频暂停时,更新按钮状态
|
||
UIController.updatePlayButton(false);
|
||
});
|
||
|
||
// 监听播放事件
|
||
AudioState.playback.streamAudioElement.addEventListener('play', () => {
|
||
// 当音频开始播放时,更新按钮状态
|
||
UIController.updatePlayButton(true);
|
||
});
|
||
|
||
// 监听错误事件
|
||
AudioState.playback.streamAudioElement.addEventListener('error', (e) => {
|
||
console.error('流式播放音频错误:', e);
|
||
UIController.updatePlayButton(false); // 更新按钮状态
|
||
this.processAudioQueue();
|
||
});
|
||
}
|
||
},
|
||
|
||
// 处理音频队列
|
||
processAudioQueue() {
|
||
// 在AudioPlayer.processAudioQueue方法中,修改队列为空时的处理
|
||
// 如果队列为空,则返回
|
||
if (AudioState.playback.audioQueue.length === 0) {
|
||
AudioState.playback.isStreamPlaying = false;
|
||
console.log('音频队列为空,停止流式播放');
|
||
|
||
// 隐藏播放界面 - 这是新增的代码
|
||
UIController.toggleElement('audioPlayer', false);
|
||
|
||
return;
|
||
}
|
||
|
||
// 从队列中取出第一个音频块
|
||
const audioBlob = AudioState.playback.audioQueue.shift();
|
||
console.log('从队列取出音频块,剩余队列长度:', AudioState.playback.audioQueue.length);
|
||
|
||
// 创建音频URL并设置为源
|
||
const audioUrl = URL.createObjectURL(audioBlob);
|
||
AudioState.playback.streamAudioElement.src = audioUrl;
|
||
|
||
// 修改AudioPlayer.processAudioQueue方法中的播放部分
|
||
AudioState.playback.streamAudioElement.play()
|
||
.then(() => {
|
||
console.log('开始播放音频块');
|
||
// 关键修复:更新播放按钮状态为播放中
|
||
UIController.updatePlayButton(true);
|
||
})
|
||
.catch(error => {
|
||
console.error('播放音频块失败:', error);
|
||
this.processAudioQueue();
|
||
})
|
||
.finally(() => {
|
||
// 播放完成后释放URL对象
|
||
setTimeout(() => {
|
||
URL.revokeObjectURL(audioUrl);
|
||
}, 1000);
|
||
});
|
||
}
|
||
};
|
||
|
||
// ==================== 事件绑定模块 ====================
|
||
const EventBinder = {
|
||
// 绑定所有事件
|
||
bindEvents() {
|
||
// 绑定录音按钮事件
|
||
const startBtn = document.getElementById('startRecordBtn');
|
||
const stopBtn = document.getElementById('stopRecordBtn');
|
||
|
||
console.log('开始绑定事件,查找按钮元素...');
|
||
console.log('开始录音按钮:', startBtn);
|
||
console.log('停止录音按钮:', stopBtn);
|
||
|
||
if (startBtn) {
|
||
startBtn.onclick = () => {
|
||
console.log('点击开始录音按钮');
|
||
RecordingManager.startRecording();
|
||
};
|
||
console.log('已绑定开始录音按钮事件');
|
||
} else {
|
||
console.error('未找到开始录音按钮');
|
||
}
|
||
|
||
if (stopBtn) {
|
||
stopBtn.onclick = () => {
|
||
console.log('点击停止录音按钮');
|
||
RecordingManager.stopRecording();
|
||
};
|
||
console.log('已绑定停止录音按钮事件');
|
||
} else {
|
||
console.error('未找到停止录音按钮');
|
||
}
|
||
}
|
||
};
|
||
|
||
// ==================== 录音管理模块 ====================
|
||
const RecordingManager = {
|
||
// 初始化录音
|
||
async initRecording() {
|
||
try {
|
||
// 获取用户媒体设备
|
||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||
|
||
// 创建媒体录制器
|
||
AudioState.recording.mediaRecorder = new MediaRecorder(stream, {
|
||
mimeType: 'audio/webm;codecs=opus'
|
||
});
|
||
|
||
// 监听数据可用事件
|
||
AudioState.recording.mediaRecorder.ondataavailable = (event) => {
|
||
if (event.data.size > 0) {
|
||
AudioState.recording.audioChunks.push(event.data);
|
||
}
|
||
};
|
||
|
||
// 监听停止事件
|
||
AudioState.recording.mediaRecorder.onstop = async () => {
|
||
console.log('录音停止,开始处理音频数据');
|
||
|
||
// 创建音频Blob
|
||
const audioBlob = new Blob(AudioState.recording.audioChunks, { type: 'audio/webm' });
|
||
console.log('录音Blob大小:', audioBlob.size);
|
||
|
||
// 更新UI
|
||
UIController.toggleElement('thinkingIndicator', true);
|
||
|
||
// 初始化WebSocket连接
|
||
WebSocketManager.initConnection();
|
||
|
||
// 等待连接建立
|
||
await this.waitForConnection();
|
||
|
||
// 发送音频数据 - 这里是错误的调用
|
||
// const success = await WebSocketManager.sendAudio(audioBlob);
|
||
|
||
// 修复后的正确调用
|
||
const success = await RecordingManager.sendAudio(audioBlob);
|
||
|
||
if (!success) {
|
||
console.error('发送音频数据失败');
|
||
UIController.toggleElement('thinkingIndicator', false);
|
||
UIController.setStartRecordButtonEnabled(true);
|
||
}
|
||
|
||
// 清空音频块
|
||
AudioState.recording.audioChunks = [];
|
||
|
||
// 停止所有音频轨道
|
||
stream.getTracks().forEach(track => track.stop());
|
||
};
|
||
|
||
console.log('录音初始化成功');
|
||
return true;
|
||
} catch (error) {
|
||
console.error('录音初始化失败:', error);
|
||
alert('录音初始化失败,请授予麦克风权限后重试');
|
||
return false;
|
||
}
|
||
},
|
||
|
||
// 开始录音
|
||
async startRecording() {
|
||
console.log('开始录音');
|
||
|
||
// 检查是否已经在录音
|
||
if (AudioState.recording.isRecording) {
|
||
console.warn('已经在录音中');
|
||
return;
|
||
}
|
||
|
||
// 初始化录音
|
||
const initialized = await this.initRecording();
|
||
if (!initialized) {
|
||
console.error('录音初始化失败,无法开始录音');
|
||
return;
|
||
}
|
||
|
||
// 开始录音
|
||
AudioState.recording.isRecording = true;
|
||
AudioState.recording.mediaRecorder.start();
|
||
|
||
// 更新UI
|
||
UIController.updateRecordingButtons(true);
|
||
|
||
console.log('录音开始成功');
|
||
|
||
// 设置最大录音时长
|
||
setTimeout(() => {
|
||
if (AudioState.recording.isRecording) {
|
||
console.log('达到最大录音时长,自动停止录音');
|
||
this.stopRecording();
|
||
}
|
||
}, AudioState.recording.maxDuration);
|
||
},
|
||
|
||
// 停止录音
|
||
stopRecording() {
|
||
console.log('停止录音');
|
||
|
||
if (!AudioState.recording.isRecording || !AudioState.recording.mediaRecorder) {
|
||
console.warn('当前没有在录音');
|
||
return;
|
||
}
|
||
|
||
// 停止录音
|
||
AudioState.recording.mediaRecorder.stop();
|
||
AudioState.recording.isRecording = false;
|
||
|
||
// 更新UI
|
||
UIController.updateRecordingButtons(false);
|
||
|
||
console.log('录音停止命令已发送');
|
||
},
|
||
|
||
// 等待WebSocket连接建立
|
||
waitForConnection() {
|
||
return new Promise((resolve) => {
|
||
const checkConnection = () => {
|
||
console.log('检查WebSocket连接状态:', AudioState.websocket.isConnected);
|
||
if (AudioState.websocket.isConnected &&
|
||
AudioState.websocket.connection &&
|
||
AudioState.websocket.connection.readyState === WebSocket.OPEN) {
|
||
console.log('WebSocket连接已建立,可以发送数据');
|
||
resolve();
|
||
} else {
|
||
console.log('WebSocket连接未建立,等待...');
|
||
setTimeout(checkConnection, 100);
|
||
}
|
||
};
|
||
|
||
checkConnection();
|
||
});
|
||
},
|
||
|
||
// 发送音频数据
|
||
async sendAudio(audioBlob) {
|
||
console.log('=== 开始执行sendAudio方法 ===');
|
||
|
||
// 参数验证
|
||
if (!audioBlob) {
|
||
console.error('sendAudio方法参数错误: audioBlob为空');
|
||
return false;
|
||
}
|
||
|
||
console.log('音频数据参数:', {
|
||
exists: !!audioBlob,
|
||
size: audioBlob.size,
|
||
type: audioBlob.type
|
||
});
|
||
|
||
// 连接状态检查
|
||
console.log('WebSocket连接状态:', {
|
||
isConnected: AudioState.websocket.isConnected,
|
||
connectionExists: !!AudioState.websocket.connection,
|
||
readyState: AudioState.websocket.connection ? AudioState.websocket.connection.readyState : 'N/A'
|
||
});
|
||
|
||
if (!AudioState.websocket.isConnected ||
|
||
!AudioState.websocket.connection ||
|
||
AudioState.websocket.connection.readyState !== WebSocket.OPEN) {
|
||
console.error('WebSocket连接未建立,无法发送音频数据');
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
console.log('将音频数据转换为Base64');
|
||
// 将音频数据转换为Base64
|
||
const base64Audio = await Utils.blobToBase64(audioBlob);
|
||
console.log('音频数据Base64长度:', base64Audio.length);
|
||
|
||
const payload = {
|
||
audio_data: base64Audio
|
||
};
|
||
console.log('准备发送的载荷:', {
|
||
keys: Object.keys(payload),
|
||
audioDataLength: payload.audio_data.length
|
||
});
|
||
|
||
// 发送音频数据
|
||
console.log('发送音频数据到WebSocket');
|
||
AudioState.websocket.connection.send(JSON.stringify(payload));
|
||
console.log('=== 音频数据发送成功 ===');
|
||
|
||
return true;
|
||
} catch (error) {
|
||
console.error('发送音频数据失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
};
|
||
|
||
// ==================== 初始化 ====================
|
||
function initializeApp() {
|
||
console.log('开始初始化学伴录音功能...');
|
||
|
||
// 初始化流式播放器
|
||
AudioPlayer.initStreamPlayer();
|
||
|
||
// 检查DOM是否已就绪
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
EventBinder.bindEvents();
|
||
console.log('学伴录音功能初始化完成(DOMContentLoaded)');
|
||
});
|
||
} else {
|
||
// DOM已经加载完成,直接绑定事件
|
||
EventBinder.bindEvents();
|
||
console.log('学伴录音功能初始化完成(直接执行)');
|
||
}
|
||
}
|
||
|
||
// 立即执行初始化
|
||
initializeApp();
|
||
|
||
// 同时保留原有的DOMContentLoaded事件作为备用
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
EventBinder.bindEvents();
|
||
console.log('学伴录音功能备用初始化完成');
|
||
});
|
||
|
||
// 页面加载完成后也尝试绑定(确保万无一失)
|
||
window.addEventListener('load', () => {
|
||
EventBinder.bindEvents();
|
||
console.log('学伴录音功能load事件初始化完成');
|
||
});
|
||
|
||
// 页面关闭时关闭WebSocket连接
|
||
window.addEventListener('beforeunload', () => {
|
||
WebSocketManager.closeConnection();
|
||
});
|