AudioContext 可异步解析原始二进制音频流(如 PCM/WAV 片段或实时 Int16/Float32 数据),无需 decodeAudioData,而是用 createBuffer 构造 AudioBuffer 并结合 AnalyserNode 实现频率可视化;需明确采样率、声道数、位深,将原始样本归一化为 [-1, 1] 的 Float32Array;对流式输入宜采用循环 AudioBuffer 定时重载或 AudioWorklet 实现低延迟处理。

AudioContext 可以异步解析原始二进制音频流(如 PCM、WAV 片段或实时采集的 Int16/Float32 数据),并结合 AnalyserNode 实现 DOM 频率可视化。关键在于:**不依赖 decodeAudioData(它要求完整、格式合法的音频文件)**,而是用 createBuffer 手动构造 AudioBuffer,再送入分析链路。
准备原始音频数据(如 PCM 流)
原始二进制音频通常为连续的采样点序列,需明确:采样率(sampleRate)、声道数(channels)、位深(如 16-bit signed integer 或 32-bit float)。例如,从 WebSocket、Fetch 流或 MediaStreamTrack.getReader() 获取的 Int16 数组:
- 若数据是
Uint8Array形式的 16-bit PCM(小端),需用Int16Array重解释:const samples = new Int16Array(raw.buffer, raw.byteOffset, raw.byteLength / 2) - 将样本值归一化到 [-1, 1] 范围(AudioBuffer 要求浮点型):
const normalized = new Float32Array(samples.length); for (let i = 0; i - 若为双声道,需按左/右交错排列,再拆分为两个通道数组传入
createBuffer
创建 AudioBuffer 并接入 AnalyserNode
用归一化后的 Float32Array 构造 AudioBuffer,并连接到 AnalyserNode 进行频域分析:
- 创建 buffer:
const buffer = audioCtx.createBuffer(channels, sampleCount, sampleRate); - 填入数据:
for (let ch = 0; ch - 创建分析节点:
const analyser = audioCtx.createAnalyser(); analyser.fftSize = 2048; analyser.smoothingTimeConstant = 0.8; - 将 buffer 播放并路由至 analyser:
const source = audioCtx.createBufferSource(); source.buffer = buffer; source.connect(analyser); analyser.connect(audioCtx.destination); source.start();
读取频率数据并驱动 DOM 可视化
AnalyserNode 提供 getByteFrequencyData(0–255 范围)或 getFloatFrequencyData(-100 至 -30 dBFS 左右),适合高频刷新:
- 分配复用的 Uint8Array:
const freqData = new Uint8Array(analyser.frequencyBinCount); - 在 requestAnimationFrame 循环中拉取:
analyser.getByteFrequencyData(freqData); - 将
freqData映射为 DOM 元素高度、颜色或 Canvas 像素值。例如绘制柱状图:bars.forEach((bar, i) => bar.style.height = `${freqData[i]}px`); - 注意:
frequencyBinCount = fftSize / 2,即 1024 个频点对应约 0–sampleRate/2 的线性频段;如需对数刻度(更符合人耳),需手动重采样索引
处理持续流式输入(非一次性 buffer)
对实时流(如 WebRTC 音频轨道或分块 fetch),不能只播一次 buffer。推荐方案是使用 ScriptProcessorNode(已废弃)的替代——AudioWorklet,或更轻量的「循环 bufferSource + 定时重载」:
- 创建一个固定长度(如 128ms)的可重写 AudioBuffer,用
audioCtx.createBufferSource()持续播放它 - 用
setTimeout或requestIdleCallback定期将新 PCM 数据写入该 buffer 的copyToChannel,并调用start(0)(时间戳设为 now)无缝衔接 - 确保写入与播放严格同步,避免爆音:可在 AudioWorklet 中实现零延迟缓冲区管理,但入门阶段用
OfflineAudioContext预处理小段再注入更稳定










