AudioContext需用户手势触发才能启动,否则报DOMException;AnalyserNode须接入音频图并设fftSize为2的幂;Canvas绘图前清空画布,用freqData映射高度绘制频谱条。
AudioContext 创建失败的常见原因
直接 new
在多数现代浏览器里会报错,典型错误是
。这是因为浏览器要求音频上下文必须由用户手势(如 click、touchstart)触发才能启动,否则静音保护机制会阻止它初始化。
实操建议:
立即学习
“
前端免费学习笔记(深入)
”;
不要在页面加载时立即执行
,改在按钮点击事件里调用
如果用 React/Vue,确保绑定到用户可交互的 DOM 元素上,比如
初始化后记得调用
(尤其 Safari 和新版 Chrome),否则后续分析节点可能不工作
用 AnalyserNode 获取实时频谱数据
是连接音频源与可视化逻辑的核心桥梁。它本身不处理或播放音频,只做 FFT 分析,输出频域或时域数组。关键点在于:它必须插入到音频图中,哪怕只是接在
前面“路过”一下。
实操建议:
立即学习
“
前端免费学习笔记(深入)
”;
创建后必须用
,再设置
(值必须是 2 的幂,常用 128–2048)
调用
前,务必先准备好对应长度的
,例如:
别漏掉连接步骤:
,否则数据不会流动
Canvas 绘制频谱条形图的最小可行代码
可视化本身不依赖第三方库,原生
+
就够用。重点是把
数组映射成高度值,并逐列绘制。
使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
下载
实操建议:
立即学习
“
前端免费学习笔记(深入)
”;
每次绘制前清空画布:
每个频点宽度固定(如 3px),循环遍历时用
作为高度比例:
避免在
回调里反复调用
之前没检查
是否已连接,否则返回全 0
示例片段:
从麦克风输入实现真·实时可视化
想让可视化响应环境声音(比如说话、拍手),得用
接麦克风。但权限和兼容性比文件播放更敏感。
实操建议:
立即学习
“
前端免费学习笔记(深入)
”;
必须用
获取流,且该 Promise 必须在用户手势后调用
拿到流后用
创建源节点,不能直接用
连接
Chrome 会自动静音未获焦点的标签页里的麦克风分析,测试时保持当前 tab 活跃
移动端 iOS Safari 对
支持有限,部分机型需开启「录音」权限并在 HTTPS 下运行
真正卡住人的往往不是算法,而是
的状态流转——
→
→
,以及分析节点是否被正确插入音频图。漏掉一次
或连错一个节点,整条链就断了,但控制台未必报错。
AudioContext()DOMException: The AudioContext was not allowed to startnew AudioContext()audioContext.resume()AnalyserNodedestinationaudioContext.createAnalyser()analyser.fftSize = 256analyser.getByteFrequencyData()Uint8Arrayconst freqData = new Uint8Array(analyser.frequencyBinCount)source.connect(analyser); analyser.connect(audioContext.destination)requestAnimationFramefreqDatactx.clearRect(0, 0, canvas.width, canvas.height)freqData[i]height = (freqData[i] / 255) * canvas.height * 0.8requestAnimationFramegetByteFrequencyData()analyser
function draw() {
analyser.getByteFrequencyData(freqData);
ctx.fillStyle = '#4a55e5';
const barWidth = 3;
for (let i = 0; i < freqData.length; i++) {
const height = (freqData[i] / 255) * canvas.height * 0.8;
ctx.fillRect(i * barWidth, canvas.height - height, barWidth, height);
}
requestAnimationFrame(draw);
}
MediaStreamAudioSourceNodenavigator.mediaDevices.getUserMedia({ audio: true })audioContext.createMediaStreamSource(stream)streamanalysergetUserMediaAudioContextsuspendedrunningclosingresume()