Vue3实时检测的录音功能

如果有人声并且大于20db,则开始录制。低于20db超过4秒,停止录制

语音实时检测

<template>
	<div class="auto-recorder">
		<canvas ref="canvas"></canvas>
		<button @click="toggleRecording" :disabled="isRecording">
			{{ isRecording ? '录音中' : '未录音' }}
		</button>

		<audio v-if="audioUrl" controls :src="audioUrl"></audio>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				isRecording: false, //显示当前录音状态
				mediaRecorder: null,
				audioChunks: [], //存储录音数据的数组
				audioContext: null, //用于处理音频
				analyser: null, //用于分析音频频谱
				bufferSize: 2048, //用于设置分析音频的缓冲区大小。
				threshold: 0, //用于设置录音阈值的属性 db
				canvasContext: null, //用于存储 Canvas 上下文的属性。
				canvasWidth: 400,
				canvasHeight: 200,
				silentTime: 0, //记录静音时间
				audioUrl: '',
				silenceInterval: null //计时器ID
			};
		},
		mounted() {
			this.setupAudio();
			this.setupCanvas();
			this.draw();
		},
		methods: {
			async setupAudio() { //异步方法,用于设置音频环境。
				//使用 navigator.mediaDevices.getUserMedia 方法请求用户媒体设备(麦克风)的权限
				//返回一个 MediaStream 对象。
				const stream = await navigator.mediaDevices.getUserMedia({
					audio: true
				});
				//创建一个新的 AudioContext 对象,用于处理音频。
				this.audioContext = new AudioContext();
				//创建 AnalyserNode 对象,用于分析音频频谱。
				this.analyser = this.audioContext.createAnalyser();
				//创建一个 MediaStreamAudioSourceNode 对象,表示从媒体流中读取数据。
				const microphone = this.audioContext.createMediaStreamSource(stream);
				//将媒体流连接到分析器节点。
				microphone.connect(this.analyser);
				//设置 FFT(Fast Fourier Transform)大小,用于控制音频数据的分析精度。
				this.analyser.fftSize = this.bufferSize;
			},
			//设置 Canvas 元素的方法
			setupCanvas() {
				// 获取组件中引用的 Canvas 元素。
				const canvas = this.$refs.canvas;
				// 获取 Canvas 2D 上下文,用于绘制图形。
				this.canvasContext = canvas.getContext('2d');
				// 设置 Canvas 宽度。
				canvas.width = this.canvasWidth;
				// 设置 Canvas 高度。
				canvas.height = this.canvasHeight;
				// 设置画布背景颜色为黑色。
				this.canvasContext.fillStyle = '#000';
				// 绘制一个填充矩形,覆盖整个画布,用黑色填充。
				this.canvasContext.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
			},
			//这是一个负责绘制音频波形图的函数,并且会检查声音的分贝是否超过阈值。
			draw() {
				//请求下一帧动画
				requestAnimationFrame(this.draw);
				if (this.analyser != null) { //当前不在录音的情况
					// 获取音频频域数据的数组长度
					const bufferLength = this.analyser.frequencyBinCount;
					// 创建一个无符号 8 位整数数组,用于存储音频频域数据
					const dataArray = new Uint8Array(bufferLength);
					// 从 AnalyserNode 获取音频频域数据
					this.analyser.getByteTimeDomainData(dataArray);
					// 设置画布的填充颜色为黑色
					this.canvasContext.fillStyle = '#000';
					// 绘制一个黑色的矩形,覆盖整个画布,用于清空画布
					this.canvasContext.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
					// 设置绘制线条的宽度为 2 像素
					this.canvasContext.lineWidth = 2;
					// 设置绘制线条的颜色为绿色
					this.canvasContext.strokeStyle = '#00ff00';
					// 开始绘制新路径
					this.canvasContext.beginPath();
					// 计算每个数据片段在画布上的宽度
					const sliceWidth = this.canvasWidth * 1.0 / bufferLength;
					// 初始化 x 坐标
					let x = 0;
					// 遍历音频频域数据数组,绘制波形图
					for (let i = 0; i < bufferLength; i++) {
						// 将音频数据归一化到 [-1, 1] 的范围
						const v = dataArray[i] / 128.0;
						// 根据归一化的数据计算波形图在画布上的 y 坐标
						const y = v * this.canvasHeight / 2;
						// 如果是第一个数据点,则将画笔移动到该点;否则,从上一个点绘制直线到当前点
						if (i === 0) {
							this.canvasContext.moveTo(x, y);
						} else {
							this.canvasContext.lineTo(x, y);
						}
						// 更新 x 坐标
						x += sliceWidth;
					}
					// 绘制直线到画布的右边中央
					this.canvasContext.lineTo(this.canvasWidth, this.canvasHeight / 2);
					// 绘制路径
					this.canvasContext.stroke();

					// 检测声音分贝是否超过阈值
					const average = this.calculateAverage(dataArray);

					// 如果平均值超过阈值
					if (average > this.threshold) {
						if (!this.isRecording) {
							this.startRecording(); //开始录音
						}
						this.silentTime = 0; //重置静音时间
					} else {
						if (this.isRecording) {
							// 开始计时,如果静音时间超过4秒,停止录音
							if (!this.silenceInterval) {
								this.silenceInterval = setInterval(() => {
									this.silentTime++;
									if (this.silentTime >= 4) {
										this.stopRecording();
										clearInterval(this.silenceInterval);
										this.silenceInterval = null;
										this.silentTime = 0; //重置静音时间
									}
								}, 1000);
							}
						}
					}
				}
			},
			// 定义了一个名为 calculateAverage 的方法,接受一个 dataArray 参数。
			calculateAverage(dataArray) {
				// 初始化一个 sum 变量用于存储音频数据的总和。
				let sum = 0;
				// 获取 dataArray 的长度并存储在 length 变量中。
				const length = dataArray.length;
				// 使用 for 循环遍历 dataArray。
				// 对于每个元素,减去128并加到 sum 上。
				// 这是因为音频数据范围在0到255之间,128表示音量的中值。
				for (let i = 0; i < length; i++) {
					sum += (dataArray[i] - 128);
				}
				// 返回音频数据的平均值。
				return sum / length;
			},
			// 控制音频录制的功能
			async toggleRecording() {
				if (!this.isRecording) {
					try {
						// 请求用户麦克风的音频输入
						const stream = await navigator.mediaDevices.getUserMedia({
							audio: true
						});

						// 创建 MediaRecorder 实例,用于录制音频
						this.mediaRecorder = new MediaRecorder(stream);


						// 添加事件监听器,当有音频数据可用时,将其存储到 audioChunks 中
						// 保存数据(数据有效)
						this.mediaRecorder.addEventListener('dataavailable', event => {
							if (event.data.size > 0) {
								this.audioChunks.push(event.data);
							}
						});
						// 开始录制媒体流
						this.mediaRecorder.start();
						this.isRecording = true;
					} catch (error) {
						console.error('录音失败...', error);
					}
				}
			},
			async stopRecording() {
				// 停止录制媒体流。
				this.mediaRecorder.stop();
				// 等待 MediaRecorder 停止录制完成
				await new Promise(resolve => {
					this.mediaRecorder.addEventListener('stop', resolve);
				});
				// 创建一个 Blob 对象,用于存储录制的音频数据
				const blob = new Blob(this.audioChunks, {
					type: 'audio/pcm'
				});
				console.log('Blob size:', blob.size);
				if (blob.size > 0) {
					console.log('Blob contains audio data');
				} else {
					console.log('Blob is empty');
				}
				this.audioUrl = URL.createObjectURL(blob);
				console.log("this.audioChunks", this.audioChunks);
				console.log("已发送给后端!");
				// 将录音数据发送给后端
				// 这里需要实现发送录音数据给后端的逻辑
				console.log('录音数据', blob);
				// 重置录音状态和录音数据数组
				this.isRecording = false;
				this.audioChunks = [];
			},
			async startRecording() {
				this.toggleRecording();
				console.log('录音中');
			}
		}
	};
</script>

<style>
	.auto-recorder {
		display: flex;
		flex-direction: column;
		align-items: center;
	}

	canvas {
		margin-top: 20px;
	}
</style>

Vue3 中并没有原生内置的音频组件可以直接用于实时录音谱,但它可以与其他库配合来实现这个功能。通常,这需要结合 Web Audio API 和一些音乐处理库,如 Tone.js 或如何js等,它们提供音频处理、MIDI事件监听等功能。 首先,你需要安装 Vue 项目并引入所需的音频处理库。然后,你可以创建一个自定义音频组件,它会监听音频流,捕获音高数据,将其转换为音符或者其他形式的音谱表示。这里是一个大概的思路: 1. **组件结构**:创建一个名为 `AudioRecorder.vue` 的组件,包含播放、暂停、录音开始/结束的按钮和一个状态指示器。 ```html <template> <div> <button @click="startRecording">开始录制</button> <button @click="stopRecording" v-if="isRecording">停止录制</button> <p>当前状态: {{ recordingStatus }}</p> <!-- 其他音频播放相关元素 --> </div> </template> ``` 2. **数据管理**:在 `data()` 函数中定义状态变量,比如 `isRecording` 表示是否正在录音,以及音谱数据。 ```javascript export default { data() { return { isRecording: false, audioData: [], recordingStatus: &#39;&#39;, }; } }; ``` 3. **方法实现**:使用 `Web Audio API` 监听音频输入,并在合适的时间更新音谱数据。 ```javascript methods: { startRecording() { // 初始化录音上下文 this.recorder = new (window.AudioContext || window.webkitAudioContext)(); // 开始录音 navigator.mediaDevices.getUserMedia({audio: true}) .then(stream => { this.audioInput = this.recorder.createMediaStreamSource(stream); // 添加节拍检测或其他音高分析逻辑 const pitchDetector = ...; pitchDetector.onPitch = this.updateSpectrum.bind(this); this.isRecording = true; this.recordingStatus = &#39;录音中&#39;; }) .catch(err => console.error(&#39;无法获取麦克风&#39;, err)); }, stopRecording() { if (!this.isRecording) return; this.isRecording = false; this.audioInput.disconnect(); // 结束录音并处理音谱数据 this.processSpectrum(); }, updateSpectrum(event) { // 更新音谱数据 this.audioData.push(event.frequency); }, processSpectrum() { // 根据音谱数据生成音符或者其他形式的音谱表示 // 这部分取决于具体的音谱算法 }, } ``` 请注意,由于涉及到复杂的音频处理技术,上述代码仅给出基础框架,实际实现可能会更复杂。此外,为了兼容性考虑,你可能还需要添加错误处理和浏览器兼容性检查。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Adellle

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值