This commit is contained in:
2025-08-31 15:14:06 +08:00
parent 5ea4f9d79c
commit 4402c1137a

View File

@@ -17,7 +17,11 @@ const AudioState = {
audioChunks: [], // 存储接收到的音频块
audioQueue: [], // 音频队列,用于流式播放
isStreamPlaying: false, // 是否正在流式播放
currentAudioIndex: 0 // 当前播放的音频索引
currentAudioIndex: 0, // 当前播放的音频索引
streamAudioElement: null, // 流式播放音频元素
totalPlayedTime: 0, // 新增:累计播放时间
estimatedTotalDuration: 0, // 新增:估算总时长
currentChunkDuration: 0 // 新增:当前块时长
},
websocket: {
connection: null,
@@ -56,7 +60,7 @@ const UIController = {
}
},
// 更新按钮状态
// 更新录音按钮状态
updateRecordingButtons(isRecording) {
this.toggleElement('recordingIndicator', isRecording);
this.toggleElement('startRecordBtn', !isRecording);
@@ -92,12 +96,46 @@ const UIController = {
}
},
// 添加总进度更新方法
updateTotalProgress() {
// 根据实际业务逻辑计算总进度
const totalProgress = AudioState.playback.totalPlayedChunks / AudioState.playback.totalChunks * 100;
this.updateProgress(totalProgress);
},
// 更新时间显示
updateTimeDisplay(currentTime, duration) {
const timeDisplay = document.getElementById('audioTime');
if (timeDisplay) {
timeDisplay.textContent = `${Utils.formatTime(currentTime)} / ${Utils.formatTime(duration)}`;
}
},
// 显示错误通知
showErrorNotification(message) {
// 创建或显示错误通知元素
let notification = document.getElementById('audioErrorNotification');
if (!notification) {
notification = document.createElement('div');
notification.id = 'audioErrorNotification';
notification.style.position = 'fixed';
notification.style.bottom = '20px';
notification.style.left = '50%';
notification.style.transform = 'translateX(-50%)';
notification.style.backgroundColor = 'rgba(255, 0, 0, 0.8)';
notification.style.color = 'white';
notification.style.padding = '10px 20px';
notification.style.borderRadius = '4px';
notification.style.zIndex = '1000';
document.body.appendChild(notification);
}
notification.textContent = message;
notification.style.display = 'block';
// 3秒后自动隐藏
setTimeout(() => {
notification.style.display = 'none';
}, 3000);
}
};
@@ -125,9 +163,15 @@ const WebSocketManager = {
};
// 连接关闭
AudioState.websocket.connection.onclose = () => {
console.log('WebSocket连接已关闭');
AudioState.websocket.connection.onclose = (event) => {
console.log('WebSocket连接已关闭,代码:', event.code, '原因:', event.reason);
AudioState.websocket.isConnected = false;
// 添加自动重连逻辑
if (!AudioState.websocket.isClosing) {
console.log('尝试重新连接WebSocket...');
setTimeout(() => WebSocketManager.initConnection(), 3000);
}
};
// 连接错误
@@ -386,18 +430,40 @@ const AudioPlayer = {
// 更新时间显示
updateTimeDisplay() {
if (!AudioState.playback.audioElement) return;
const currentTime = AudioState.playback.audioElement.currentTime;
const duration = AudioState.playback.audioElement.duration;
UIController.updateTimeDisplay(currentTime, duration);
},
// 在AudioPlayer.initStreamPlayer方法中添加暂停事件监听
// 流式播放进度更新 - 新增方法
updateStreamProgress() {
if (!AudioState.playback.streamAudioElement) return;
// 计算当前音频块播放进度
const currentTime = AudioState.playback.streamAudioElement.currentTime;
const duration = AudioState.playback.streamAudioElement.duration || AudioState.playback.currentChunkDuration;
// 更新进度条
const progress = (currentTime / duration) * 100;
UIController.updateProgress(progress);
// 更新时间显示(累计已播放时间 + 当前块播放时间)
const totalPlayedTime = AudioState.playback.totalPlayedTime + currentTime;
UIController.updateTimeDisplay(totalPlayedTime, AudioState.playback.estimatedTotalDuration);
},
// 初始化流式播放器
initStreamPlayer() {
// 创建新的音频元素用于流式播放
if (!AudioState.playback.streamAudioElement) {
AudioState.playback.streamAudioElement = new Audio();
// 添加暂停按钮点击事件绑定
const playBtn = document.getElementById('playAudioBtn');
if (playBtn) {
playBtn.onclick = () => AudioPlayer.togglePlay();
}
// 监听音频结束事件
AudioState.playback.streamAudioElement.addEventListener('ended', () => {
@@ -424,6 +490,11 @@ const AudioPlayer = {
UIController.updatePlayButton(false); // 更新按钮状态
this.processAudioQueue();
});
// 添加进度更新事件监听 - 新增代码
AudioState.playback.streamAudioElement.addEventListener('timeupdate', () => {
this.updateStreamProgress();
});
}
},
@@ -449,15 +520,29 @@ const AudioPlayer = {
const audioUrl = URL.createObjectURL(audioBlob);
AudioState.playback.streamAudioElement.src = audioUrl;
// 添加当前音频块时长跟踪
AudioState.playback.currentChunkStartTime = Date.now();
AudioState.playback.currentChunkDuration = audioBlob.size / 1024 / 16; // 估算时长
// 修改AudioPlayer.processAudioQueue方法中的播放部分
AudioState.playback.streamAudioElement.play()
.then(() => {
console.log('开始播放音频块');
// 关键修复:更新播放按钮状态为播放中
UIController.updatePlayButton(true);
// 更新总进度 - 修改前this.updateTotalProgress();
UIController.updateTotalProgress(); // 修改为直接调用UIController方法
})
.catch(error => {
console.error('播放音频块失败:', error);
// 添加失败后状态重置
AudioState.playback.isStreamPlaying = false;
UIController.updatePlayButton(false);
// 添加用户可见的错误提示
UIController.showErrorNotification('音频播放失败,请重试');
this.processAudioQueue();
})
.finally(() => {
@@ -598,6 +683,8 @@ const RecordingManager = {
setTimeout(() => {
if (AudioState.recording.isRecording) {
console.log('达到最大录音时长,自动停止录音');
// 添加用户提示
alert('已达到最大录音时长');
this.stopRecording();
}
}, AudioState.recording.maxDuration);
@@ -624,8 +711,16 @@ const RecordingManager = {
// 等待WebSocket连接建立
waitForConnection() {
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
const maxRetries = 30; // 最多等待3秒
let retries = 0;
const checkConnection = () => {
if (retries >= maxRetries) {
reject(new Error('WebSocket连接超时'));
return;
}
console.log('检查WebSocket连接状态:', AudioState.websocket.isConnected);
if (AudioState.websocket.isConnected &&
AudioState.websocket.connection &&
@@ -634,6 +729,7 @@ const RecordingManager = {
resolve();
} else {
console.log('WebSocket连接未建立等待...');
retries++;
setTimeout(checkConnection, 100);
}
};
@@ -706,23 +802,23 @@ function initializeApp() {
// 初始化流式播放器
AudioPlayer.initStreamPlayer();
// 检查DOM是否已就绪
// 统一使用DOMContentLoaded事件绑定
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
EventBinder.bindEvents();
console.log('学伴录音功能初始化完成DOMContentLoaded');
});
document.addEventListener('DOMContentLoaded', bindEventsAndInitialize);
} else {
// DOM已经加载完成直接绑定事件
EventBinder.bindEvents();
console.log('学伴录音功能初始化完成(直接执行)');
bindEventsAndInitialize();
}
}
function bindEventsAndInitialize() {
EventBinder.bindEvents();
console.log('学伴录音功能初始化完成');
}
// 立即执行初始化
initializeApp();
// 同时保留原有的DOMContentLoaded事件作为备用
// 移除重复的DOMContentLoaded和load事件绑定
document.addEventListener('DOMContentLoaded', () => {
EventBinder.bindEvents();
console.log('学伴录音功能备用初始化完成');
@@ -736,5 +832,20 @@ window.addEventListener('load', () => {
// 页面关闭时关闭WebSocket连接
window.addEventListener('beforeunload', () => {
WebSocketManager.closeConnection();
WebSocketManager.closeConnection(true);
// 清理音频元素
if (AudioState.playback.streamAudioElement) {
// 移除所有事件监听器
const oldElement = AudioState.playback.streamAudioElement;
const newElement = new Audio();
AudioState.playback.streamAudioElement = newElement;
oldElement.remove();
}
// 清理URL对象
if (AudioState.playback.audioUrl) {
URL.revokeObjectURL(AudioState.playback.audioUrl);
AudioState.playback.audioUrl = null;
}
});