Web录音并实现声纹可视化(vue3)_vue3 录音并显示声波的组件
本文主要实现web端,调用电脑音频权限,并且将录制声音的声纹变化进行可视化展示,话不多说上代码。
实现效果
页面样式根据自己的需求调整,本文只做简单功能实现
依赖安装
npm install recordrtc
代码部分
{{ isRecording ? \'停止录音\' : \'开始录音\' }}
/** * 音频录制与可视化组件 * * 功能: * 1. 录制用户麦克风音频 * 2. 实时显示音频波形动画 * 3. 提供录音控制功能 */import { ref, onUnmounted, onMounted } from \'vue\'import RecordRTC from \'recordrtc\'// 音频处理相关变量let audioContext = null // 音频上下文let analyser = null // 音频分析器let dataArray = null // 音频数据数组let recorder = null // 录音器实例let stream = null // 媒体流// 动画相关变量const waveCanvas = ref(null) // 波形画布引用let ctx = null // 画布上下文let animationFrameId = null // 动画帧ID// 状态变量const isRecording = ref(false) // 录音状态const audioLevel = ref(0) // 音频音量级别/** * 调整画布大小以适应容器 */const resizeCanvas = () => { if (!waveCanvas.value) return const container = waveCanvas.value.parentElement waveCanvas.value.width = container.clientWidth waveCanvas.value.height = 150 // 固定高度或根据需要调整}/** * 组件挂载时初始化画布 */onMounted(() => { if (waveCanvas.value) { // 初始化画布上下文 ctx = waveCanvas.value.getContext(\'2d\') // 初始调整画布大小 resizeCanvas() // 监听窗口大小变化,调整画布大小 window.addEventListener(\'resize\', resizeCanvas) }})/** * 组件卸载时清理资源 */onUnmounted(() => { stopRecording() window.removeEventListener(\'resize\', resizeCanvas)})/** * 切换录音状态 */const toggleRecording = () => { if (isRecording.value) { stopRecording() } else { startRecording() }}/** * 开始录音并初始化音频分析 */const startRecording = async () => { // 检查浏览器是否支持媒体设备 if (!navigator.mediaDevices) { console.error(\'当前浏览器不支持 mediaDevices API\') return } try { // 请求麦克风权限并获取音频流 stream = await navigator.mediaDevices.getUserMedia({ audio: true }) // 初始化音频上下文和分析器 audioContext = new (window.AudioContext || window.webkitAudioContext)() const source = audioContext.createMediaStreamSource(stream) analyser = audioContext.createAnalyser() analyser.fftSize = 512 // 设置FFT大小,值越大频率分析越精细 source.connect(analyser) dataArray = new Uint8Array(analyser.frequencyBinCount) // 配置录音选项 const options = { type: \'audio\', mimeType: \'audio/wav\', recorderType: RecordRTC.StereoAudioRecorder, numberOfAudioChannels: 1, desiredSampRate: 24000, checkForInactiveTracks: true } // 创建并启动录音器 recorder = new RecordRTC(stream, options) recorder.startRecording() // 更新状态并开始动画 isRecording.value = true analyzeAudio() drawWave() } catch (error) { console.error(\'获取麦克风权限失败\', error) }}/** * 停止录音并清理资源 */const stopRecording = () => { if (!isRecording.value) return // 停止录音 if (recorder) { recorder.stopRecording(() => { // 获取录音数据 const blob = recorder.getBlob() // 这里可以添加保存或发送录音数据的逻辑 console.log(\'录音完成,数据大小:\', blob.size) }) } // 停止动画 if (animationFrameId) { cancelAnimationFrame(animationFrameId) animationFrameId = null } // 关闭音频流 if (stream) { stream.getTracks().forEach(track => track.stop()) stream = null } // 关闭音频上下文 if (audioContext) { if (audioContext.state !== \'closed\') { audioContext.close().catch(err => console.error(\'关闭音频上下文失败\', err)) } audioContext = null } // 重置状态 analyser = null dataArray = null recorder = null isRecording.value = false audioLevel.value = 0}/** * 分析音频数据并计算音量级别 */const analyzeAudio = () => { if (!analyser || !dataArray || !isRecording.value) return // 获取频率数据 analyser.getByteFrequencyData(dataArray) // 计算平均音量 const average = dataArray.reduce((acc, val) => acc + val, 0) / dataArray.length // 将音量映射到0-100范围 audioLevel.value = Math.min(100, (average / 256) * 100) // 继续下一帧分析 if (isRecording.value) { requestAnimationFrame(analyzeAudio) }}/** * 绘制音频波形动画 */const drawWave = () => { if (!ctx || !waveCanvas.value || !dataArray || !analyser || !isRecording.value) return const { width, height } = waveCanvas.value // 清除画布 ctx.clearRect(0, 0, width, height) // 创建渐变色 const gradient = ctx.createLinearGradient(0, 0, width, 0) gradient.addColorStop(0, \'#6366f1\') // indigo-500 gradient.addColorStop(0.5, \'#a855f7\') // purple-500 gradient.addColorStop(1, \'#8b5cf6\') // violet-500 // 获取最新的音频数据 analyser.getByteFrequencyData(dataArray) // 计算音量平均值,用于动态调整波形振幅 const average = dataArray.reduce((acc, val) => acc + val, 0) / dataArray.length const volumeMultiplier = Math.max(0.5, average / 128) // 绘制主波形 ctx.strokeStyle = gradient ctx.lineWidth = 3 ctx.beginPath() // 使用时间和音频数据创建动态波形 const time = Date.now() * 0.05 / 1000 for (let x = 0; x < width; x++) { // 将x坐标映射到音频数据索引 const dataIndex = Math.floor((x / width) * dataArray.length) // 计算音频因子(0-1) const audioFactor = dataArray[dataIndex] / 255 // 计算波形高度,结合时间和音频数据 const y = height / 2 + Math.sin(x * 0.02 + time) * 20 * volumeMultiplier * (0.5 + audioFactor) + Math.sin(x * 0.03 + time * 1.3) * 10 * volumeMultiplier * audioFactor if (x === 0) { ctx.moveTo(x, y) } else { ctx.lineTo(x, y) } } ctx.stroke() // 绘制辅助波形(更细、更透明) ctx.strokeStyle = \'rgba(192, 132, 252, 0.4)\' // purple-400 with opacity ctx.lineWidth = 1.5 ctx.beginPath() for (let x = 0; x < width; x++) { const dataIndex = Math.floor((x / width) * dataArray.length) const audioFactor = dataArray[dataIndex] / 255 const y = height / 2 + Math.sin(x * 0.014 - time * 0.8) * 14 * volumeMultiplier * audioFactor if (x === 0) { ctx.moveTo(x, y) } else { ctx.lineTo(x, y) } } ctx.stroke() // 绘制音量指示器(频谱柱状图) const barWidth = 3 const barGap = 3 const barCount = Math.floor(width / (barWidth + barGap)) const barMaxHeight = height * 0.7 ctx.fillStyle = gradient for (let i = 0; i 0) { // 绘制圆角矩形 const x = i * (barWidth + barGap) const y = height - barHeight const radius = barWidth / 2 ctx.beginPath() ctx.moveTo(x + radius, y) ctx.lineTo(x + barWidth - radius, y) ctx.quadraticCurveTo(x + barWidth, y, x + barWidth, y + radius) ctx.lineTo(x + barWidth, height - radius) ctx.quadraticCurveTo(x + barWidth, height, x + barWidth - radius, height) ctx.lineTo(x + radius, height) ctx.quadraticCurveTo(x, height, x, height - radius) ctx.lineTo(x, y + radius) ctx.quadraticCurveTo(x, y, x + radius, y) ctx.closePath() ctx.fill() } } // 继续下一帧动画 if (isRecording.value) { animationFrameId = requestAnimationFrame(drawWave) }}
.audio-recorder { width: 100%; padding: 20px;}
这样一个可视化的声纹展示与录音功能就实现啦,如果对你有帮助记得三连哦~
另附上recordrtc文档地址https://recordrtc.org/