功能实现:
- 获取用户的音频输入,用于自动检测是否在说话和录音。
- 检测音频输入的声音变化,以确定是否在说话。
- 在说话时,开始录音。 当检测到说话停止时,停止录音并发送录音数据。
- 将录音数据发送到后端进行处理,并根据处理结果更新界面显示。
- 在界面上实现声音变化的动画效果。
<template>
<div>
<h1>音频输入和录音</h1>
<!-- 录音按钮,根据是否正在录音显示不同文本 -->
<button @click="toggleRecording">
{{ isRecording ? '停止录音' : '开始录音' }}
</button>
<!-- 当正在录音时显示录音状态和音量条 -->
<div v-if="isRecording">
<p>录音中...</p>
<div
:style="{
height: '10px',
width: '100%',
backgroundColor: 'blue',
transform: `scaleX(${volumeLevel})`,
}"
></div>
</div>
<!-- 如果有录音完成,显示音频控件 -->
<audio v-if="audioUrl" controls :src="audioUrl"></audio>
<div>{{ volumeLevel }}</div>
</div>
</template>
<script>
export default {
data() {
return {
audioContext: null, // 音频上下文,用于处理音频数据
mediaRecorder: null, // 媒体录音器,用于录音
audioChunks: [], // 存储音频数据块
isRecording: false, // 标记是否正在录音
audioStream: null, // 音频流
audioUrl: '', // 录制完成的音频的URL
volumeLevel: 0, // 音量等级,用于显示音量条
lastSoundTime: 0, // 记录最后检测到声音的时间
silenceTimer: null, // 静默检测定时器
maxVolumeThreshold: 0.01, // 最大声音阈值
};
},
methods: {
// 切换录音状态
async toggleRecording() {
if (!this.isRecording) {
await this.startRecording();
} else {
this.stopRecording();
}
},
// 开始录音
async startRecording() {
if (!navigator.mediaDevices) {
alert('浏览器不支持音频输入设备!');
return;
}
this.audioStream = await navigator.mediaDevices.getUserMedia({
audio: true,
});
this.mediaRecorder = new MediaRecorder(this.audioStream);
this.audioContext = new AudioContext();
// 创建音频源
const source = this.audioContext.createMediaStreamSource(
this.audioStream
);
// 实时音量监控和噪声门控
const analyser = this.audioContext.createAnalyser();
source.connect(analyser);
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
this.mediaRecorder.ondataavailable = (e) => this.audioChunks.push(e.data);
this.mediaRecorder.onstop = this.handleRecordingStop;
this.mediaRecorder.start();
this.isRecording = true;
// 初始化lastSoundTime为当前时间
this.lastSoundTime = Date.now(); // 改动点:确保开始记录时间以避免立即停止
const checkAudio = () => {
// 读取音频数据,计算当前音量
analyser.getByteTimeDomainData(dataArray);
let sum = 0;
for (let i = 0; i < bufferLength; i++) {
sum += Math.abs(dataArray[i] - 128); // 计算偏离中心点的绝对值之和
}
let currentVolumeLevel = sum / bufferLength / 128; // 计算归一化音量水平
this.volumeLevel = currentVolumeLevel;
if (currentVolumeLevel > this.maxVolumeThreshold) {
// 检测到声音的阈值
this.lastSoundTime = Date.now(); // 更新最后声音检测时间
if (this.silenceTimer) {
clearTimeout(this.silenceTimer); // 如果有声音,则清除现有的定时器
}
}
if (this.isRecording) {
requestAnimationFrame(checkAudio); // 继续实时检测声音
}
// 设置定时器检查是否应该停止录音
this.silenceTimer = setTimeout(() => {
if (Date.now() - this.lastSoundTime > 2000) {
// 如果2秒内无声音
this.stopRecording(); // 自动停止录音
}
}, 2100); // 设置超时时间略长于2秒,以确保时间足够,防止误操作
};
checkAudio();
},
// 停止录音
stopRecording() {
this.mediaRecorder.stop();
this.audioStream.getTracks().forEach((track) => track.stop());
this.isRecording = false;
clearTimeout(this.silenceTimer); // 清除定时器
},
// 录音停止后的处理
handleRecordingStop() {
const audioBlob = new Blob(this.audioChunks);
this.audioUrl = URL.createObjectURL(audioBlob);
this.audioChunks = [];
this.sendAudioToServer(audioBlob); // 发送音频数据到服务器
},
// 将录音发送到服务器
async sendAudioToServer(audioBlob) {
console.log('音频数据已发送到服务器');
return;
const formData = new FormData();
formData.append('audio', audioBlob);
try {
const response = await fetch('YOUR_BACKEND_URL', {
method: 'POST',
body: formData,
});
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
},
},
};
</script>