2025-02-22 21:04:37 +08:00
|
|
|
// audio-transmitter.js
|
2025-03-26 17:10:39 +08:00
|
|
|
|
2025-02-22 21:04:37 +08:00
|
|
|
export default {
|
2025-03-26 17:10:39 +08:00
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
ws: null, // WebSocket 连接
|
|
|
|
mediaRecorder: null, // MediaRecorder 实例
|
|
|
|
audioChunks: [], // 音频数据
|
|
|
|
status: '点击按钮开始传输音频',
|
|
|
|
isRecording: false, // 用于判断是否在录音
|
|
|
|
audioConfig: {
|
|
|
|
sampleRate: 44100, // 固定为 44100Hz
|
|
|
|
bitsPerSample: 16, // 固定为 16 位深度
|
|
|
|
channels: 2 // 固定为双声道
|
|
|
|
},
|
|
|
|
stream: null,
|
|
|
|
audioContext: null,
|
|
|
|
worklet: null,
|
|
|
|
lastSendTime: 0,
|
|
|
|
sendInterval: 200, // 数据传输间隔(毫秒)
|
|
|
|
audioWs: null, // 音频 WebSocket 连接
|
|
|
|
};
|
|
|
|
},
|
|
|
|
beforeDestroy() {
|
|
|
|
// 组件销毁前清理资源
|
|
|
|
this.cleanup();
|
|
|
|
|
|
|
|
// 关闭主 WebSocket 连接
|
|
|
|
if (this.ws) {
|
|
|
|
console.log('关闭主 WebSocket 连接');
|
|
|
|
this.ws.close();
|
|
|
|
this.ws = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
toggleRecording() {
|
|
|
|
if (this.isRecording) {
|
|
|
|
this.stopRecording();
|
|
|
|
} else {
|
|
|
|
this.startRecording();
|
|
|
|
}
|
2025-02-22 21:04:37 +08:00
|
|
|
},
|
2025-03-26 17:10:39 +08:00
|
|
|
|
|
|
|
async startRecording() {
|
|
|
|
if (this.isRecording) return;
|
|
|
|
|
|
|
|
console.log('音频传输器: 开始录音');
|
|
|
|
this.status = '开始录音并传输音频...';
|
|
|
|
this.isRecording = true;
|
|
|
|
|
|
|
|
const constraints = {
|
|
|
|
audio: {
|
|
|
|
sampleRate: this.audioConfig.sampleRate,
|
|
|
|
channelCount: this.audioConfig.channels,
|
|
|
|
sampleSize: this.audioConfig.bitsPerSample
|
2025-02-22 21:04:37 +08:00
|
|
|
}
|
2025-03-26 17:10:39 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
console.log('使用的音频配置:', constraints);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// 直接获取麦克风权限,不再重新建立 WebSocket 连接
|
|
|
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
|
|
console.log('成功获取麦克风权限');
|
|
|
|
|
|
|
|
const audioContext = new AudioContext({
|
|
|
|
sampleRate: this.audioConfig.sampleRate
|
|
|
|
});
|
|
|
|
|
|
|
|
await audioContext.audioWorklet.addModule('audio-processor.js');
|
|
|
|
|
|
|
|
const source = audioContext.createMediaStreamSource(stream);
|
|
|
|
const worklet = new AudioWorkletNode(audioContext, 'audio-processor');
|
|
|
|
|
|
|
|
worklet.port.onmessage = (event) => {
|
|
|
|
if (this.audioWs && this.audioWs.readyState === WebSocket.OPEN) {
|
|
|
|
const currentTime = Date.now();
|
|
|
|
if (!this.lastSendTime || currentTime - this.lastSendTime > this.sendInterval) {
|
|
|
|
const pcmData = new Int16Array(event.data);
|
|
|
|
console.log('发送的音频数据:', Array.from(pcmData.slice(0, 20)));
|
|
|
|
this.audioWs.send(event.data);
|
|
|
|
this.lastSendTime = currentTime;
|
2025-02-22 21:04:37 +08:00
|
|
|
}
|
|
|
|
}
|
2025-03-26 17:10:39 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
source.connect(worklet);
|
|
|
|
worklet.connect(audioContext.destination);
|
|
|
|
|
|
|
|
this.stream = stream;
|
|
|
|
this.audioContext = audioContext;
|
|
|
|
this.worklet = worklet;
|
|
|
|
} catch (error) {
|
|
|
|
console.error('音频初始化失败:', error);
|
|
|
|
this.cleanup();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// stopRecording() {
|
|
|
|
// console.log('音频传输器: 停止录音');
|
|
|
|
// this.status = '停止录音';
|
|
|
|
// this.isRecording = false;
|
|
|
|
|
|
|
|
// // 关闭音频资源
|
|
|
|
// if (this.stream) {
|
|
|
|
// console.log('关闭音频流');
|
|
|
|
// this.stream.getTracks().forEach(track => track.stop());
|
|
|
|
// }
|
|
|
|
// if (this.worklet) {
|
|
|
|
// console.log('断开 AudioWorklet');
|
|
|
|
// this.worklet.disconnect();
|
|
|
|
// }
|
|
|
|
// if (this.audioContext) {
|
|
|
|
// console.log('关闭音频上下文');
|
|
|
|
// this.audioContext.close();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // 关闭音频 WebSocket 连接
|
|
|
|
// if (this.audioWs) {
|
|
|
|
// console.log('关闭音频 WebSocket 连接');
|
|
|
|
// this.audioWs.close();
|
|
|
|
// this.audioWs = null;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// 修改停止录音方法
|
|
|
|
stopRecording() {
|
|
|
|
console.log('音频传输器: 停止录音');
|
|
|
|
this.status = '停止录音';
|
|
|
|
this.isRecording = false;
|
|
|
|
|
|
|
|
// 异步关闭防止状态冲突
|
|
|
|
const cleanup = async () => {
|
|
|
|
// 优先关闭音频流
|
2025-02-22 21:04:37 +08:00
|
|
|
if (this.stream) {
|
|
|
|
console.log('关闭音频流');
|
|
|
|
this.stream.getTracks().forEach(track => track.stop());
|
2025-03-26 17:10:39 +08:00
|
|
|
this.stream = null;
|
2025-02-22 21:04:37 +08:00
|
|
|
}
|
2025-03-26 17:10:39 +08:00
|
|
|
|
|
|
|
// 断开音频处理节点
|
2025-02-22 21:04:37 +08:00
|
|
|
if (this.worklet) {
|
|
|
|
console.log('断开 AudioWorklet');
|
|
|
|
this.worklet.disconnect();
|
2025-03-26 17:10:39 +08:00
|
|
|
this.worklet = null;
|
2025-02-22 21:04:37 +08:00
|
|
|
}
|
2025-03-26 17:10:39 +08:00
|
|
|
|
|
|
|
// 安全关闭音频上下文
|
|
|
|
if (this.audioContext && this.audioContext.state !== 'closed') {
|
2025-02-22 21:04:37 +08:00
|
|
|
console.log('关闭音频上下文');
|
2025-03-26 17:10:39 +08:00
|
|
|
await this.audioContext.close();
|
|
|
|
this.audioContext = null;
|
2025-02-22 21:04:37 +08:00
|
|
|
}
|
2025-03-26 17:10:39 +08:00
|
|
|
|
|
|
|
// 移除关闭 WebSocket 的代码
|
|
|
|
// if (this.audioWs) {
|
|
|
|
// console.log('关闭音频 WebSocket 连接');
|
|
|
|
// this.audioWs.close();
|
|
|
|
// this.audioWs = null;
|
|
|
|
// }
|
|
|
|
};
|
|
|
|
|
|
|
|
cleanup().catch(error => {
|
|
|
|
console.error('清理过程中发生错误:', error);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
async cleanup() {
|
|
|
|
console.log('开始清理音频资源...');
|
|
|
|
|
|
|
|
// 停止录音
|
|
|
|
if (this.isRecording) {
|
|
|
|
this.isRecording = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 优先关闭音频流
|
|
|
|
if (this.stream) {
|
|
|
|
console.log('关闭音频流');
|
|
|
|
this.stream.getTracks().forEach(track => track.stop());
|
|
|
|
this.stream = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 断开音频处理节点
|
|
|
|
if (this.worklet) {
|
|
|
|
console.log('断开 AudioWorklet');
|
|
|
|
this.worklet.disconnect();
|
|
|
|
this.worklet = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 安全关闭音频上下文
|
|
|
|
if (this.audioContext && this.audioContext.state !== 'closed') {
|
|
|
|
console.log('关闭音频上下文');
|
|
|
|
await this.audioContext.close();
|
|
|
|
this.audioContext = null;
|
2025-02-22 21:04:37 +08:00
|
|
|
}
|
2025-03-26 17:10:39 +08:00
|
|
|
|
|
|
|
// 关闭音频 WebSocket 连接
|
|
|
|
// if (this.audioWs) {
|
|
|
|
// console.log('关闭音频 WebSocket 连接');
|
|
|
|
// this.audioWs.close();
|
|
|
|
// this.audioWs = null;
|
|
|
|
// }
|
2025-02-22 21:04:37 +08:00
|
|
|
}
|
2025-03-26 17:10:39 +08:00
|
|
|
}
|
|
|
|
};
|