AudioContext 启动需用户手势触发,跨域资源需 CORS 头,decodeAudioData 前须确保 ArrayBuffer 有效且只调用一次,BufferSource 播放后不可复用,音量调节应使用 setTargetAtTime 避免跳变。
现代浏览器默认禁止未交互触发的音频上下文启动,new AudioContext() 可能创建成功但后续调用 resume() 报错 DOMException: The audio context was not allowed to start。
click、touchstart)回调中调用 audioContext.resume()
DOMContentLoaded 或 setTimeout 里直接 resumeAccess-Control-Allow-Origin: *,否则 decodeAudioData() 会静默失败fetch() + arrayBuffer() 获取音频二进制后,传给 audioContext.decodeAudioData(),常见错误是把 response.arrayBuffer() 直接 await 两次——第二次返回空 ArrayBuffer,导致解码失败且无报错。
arrayBuffer(),并缓存结果ArrayBuffer.byteLength > 0
catch 中打印 err.message,常见提示是 Failed to decode audio data,大概率是格式不支持(如 Safari 不支持 MP3 解码)或数据损坏fetch('sound.mp3')
.then(r => r.arrayBuffer())
.then(buffer => audioContext.decodeAudioData(buffer))
.then(audioBuffer => {
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start(); // 必须在 resume() 之后调用
})
.catch(err => console.error('Decode failed:', err.message));
AudioBufferSourceNode 是一次性节点:调用 start() 后,再次 start() 会抛出 InvalidStateError。不能复用同一个 source 节点反复播放。
audioContext.createBufferSource()
source.loop = true,但注意手动 stop() 避免内存泄漏直接赋值 gainNode.gain.value = 0.5 会导致跳变,尤其在实时调节(如拖拽音量条)时明显咔哒声。
gainNode.gain.setTargetAtTime(target, audioContext.currentTime, timeConstant)
timeConstant 通常取 0.01~0.1,越小过渡越快,但太小仍可能有瞬态失真exponentialRampToValueAtTime() 或 setValueCurveAtTime()
命周期和浏览器策略。最常卡住的地方不是“怎么写”,而是“什么时候允许写”——比如 resume() 必须由用户操作触发,这个约束在桌面端测试容易忽略,一到移动端就全崩。