浏览器无法直接转换音频文件为波形图,需通过filereader读取arraybuffer,audiocontext.decodeaudiodata解码后获取时域数据绘制;必须在用户交互后创建audiocontext以防ios/safari静音;解码得audiobuffer可反复读取,适合静态波形生成。

用 AudioContext + analyserNode 实时分析音频数据
浏览器不能直接“转换”音频文件为波形图,而是读取音频数据后自己画。核心路径是:用户选文件 → 用 FileReader 读成 ArrayBuffer → 交给 AudioContext.decodeAudioData() 解码 → 获取时域数据(getByteTimeDomainData)→ 用 <canvas></canvas> 绘制。注意:必须在用户交互(如 click)后创建 AudioContext,否则 iOS/Safari 会静音或报错 DOMException: The audio context was not allowed to start。
为什么不用 createMediaElementSource?
因为用户上传的是本地 File 对象,不是已播放的 <audio></audio> 元素。强行绑定 MediaElementSource 需先设 src 为 URL.createObjectURL(file),再等 loadedmetadata,再播放——但波形图通常不需要播放,且触发播放会带来自动播放策略限制(尤其移动端)。直接解码更可控、更轻量。
- 解码后得到
AudioBuffer,可反复读取,适合静态波形生成 - 若需实时响应(如拖动进度条重绘),才考虑
MediaElementSource+analyser的组合 -
decodeAudioData()返回 Promise,失败时可能因格式不支持(如 Safari 不支持某些 FLAC 变体)
绘制波形的关键:采样点压缩与归一化
一个 1 分钟的 WAV 文件解码后可能有上百万个采样点(44.1kHz × 60s ≈ 2.6M),全画会卡死。必须降采样:比如每 100 个点取最大值和最小值,构成“峰谷线”。同时原始数据范围是 [-1, 1],需映射到 canvas 像素坐标(如 y=50~150)。
const channelData = audioBuffer.getChannelData(0); // 取左声道
const step = Math.ceil(channelData.length / canvasWidth);
const waveData = [];
for (let i = 0; i max) max = val;
}
waveData.push({ min, max });
}
常见坑:跨域音频、内存泄漏、Canvas 清除
本地文件无跨域问题,但若后续想支持远程 URL,需确保服务端返回 Access-Control-Allow-Origin,否则 decodeAudioData() 拒绝解析。每次重新绘图前忘记调用 ctx.clearRect(0, 0, width, height) 会导致旧波形残留。另外,AudioContext 不会自动关闭,长期页面不刷新可能积累多个实例,建议在绘制完成后调用 audioContext.close()(除非你还要复用它做其他音频处理)。
- Android Chrome 有时对大文件(>50MB)解码失败,错误信息为
UnknownError,可先用file.size做前端拦截 - Firefox 对
getByteTimeDomainData的 buffer 长度要求严格(必须是 2 的幂),而 Chrome 宽松些,统一用new Uint8Array(analyser.frequencyBinCount)更稳妥 - canvas 的
height设太小(如 100px)会让波形扁平难辨,建议 ≥ 200px 并留出上下边距
真正麻烦的不是画一条线,而是让不同长度、不同响度的音频在视觉上“看起来差不多”。这需要动态计算 RMS 或峰值阈值做归一化,而不是简单拉伸 —— 这一步容易被跳过,结果就是安静的人声波形几乎看不见,爆炸音效却顶满画布。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











