You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何消除JavaScript连续播放Audio数组音频时的短延迟

问题根源

使用独立Audio对象搭配ended事件回调触发下一段音频播放的实现,天然存在不可消除的毫秒级间隙:

  • ended事件从音频播完触发到回调函数执行,会经过浏览器事件循环调度,存在固定延迟
  • 每个独立Audio对象启动播放时,都需要走资源状态校验、音频解码管线启动的流程,哪怕文件已经提前缓存也会产生开销
  • 浏览器没有为独立Audio元素的连续播放做无缝衔接优化,数毫秒的空窗是该方案的固有缺陷,无法通过微调事件监听逻辑解决。
最优解决方案:基于Web Audio API实现无缝播放

Web Audio API是浏览器提供的低延迟音频处理接口,支持将音频提前解码为内存中的音频缓冲区,基于微秒级精度的音频时钟做精准调度,可以完全消除音频衔接的间隙。

实现逻辑

  • 初始化Web Audio音频上下文,提前将所有.wav文件拉取并解码为AudioBuffer存入数组,替代原来的Audio对象数组
  • 放弃依赖ended事件的触发式播放逻辑,以音频上下文的currentTime作为统一时间基准,上一段音频的结束时间点直接作为下一段音频的播放起始点,提前完成调度
  • 动态添加音频时,同样将目标音频拉取解码为AudioBuffer加入数组即可

重构后的参考代码

let audioCtx;
// 存储解码完成的音频缓冲区
const soundBuffers = [];
let currentIndex = -1;
let nextStartTime = 0;

// 初始化音频上下文(需要在用户首次交互后调用,符合浏览器自动播放策略)
function initAudioContext() {
  if (!audioCtx) {
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  }
}

// 加载并解码单个音频文件
async function loadSound(url) {
  const res = await fetch(url);
  const arrayBuffer = await res.arrayBuffer();
  const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
  soundBuffers.push(audioBuffer);
}

// 按顺序调度播放
function scheduleNext() {
  currentIndex++;
  if (currentIndex >= soundBuffers.length) {
    currentIndex = -1;
    return;
  }
  const source = audioCtx.createBufferSource();
  source.buffer = soundBuffers[currentIndex];
  source.connect(audioCtx.destination);
  // 精准卡在上一段结束的时间点启动播放
  source.start(nextStartTime);
  // 更新下一段的起始时间为当前音频的结束时间
  nextStartTime += source.buffer.duration;
  // 当前音频播完后自动调度下一段
  source.onended = scheduleNext;
}

// 启动播放
function playSnd() {
  initAudioContext();
  // 音频上下文被挂起时先恢复
  if (audioCtx.state === 'suspended') {
    audioCtx.resume();
  }
  currentIndex = -1;
  nextStartTime = audioCtx.currentTime;
  scheduleNext();
}

// 动态添加音频
async function addSound(audioUrl) {
  initAudioContext();
  await loadSound(audioUrl);
}

// 页面初始化时在首次用户交互后预加载所有初始音频
window.addEventListener('click', () => {
  initAudioContext();
  // 此处替换为Thymeleaf模板渲染的初始音频路径列表,依次调用loadSound加载即可
}, { once: true });

注意事项

  • 必须在用户首次点击、按键等交互事件里初始化/恢复音频上下文,否则会被浏览器自动播放策略拦截
  • 初始音频尽量全部预加载解码完成后再触发播放,避免播放过程中解码导致卡顿
  • 所有播放时间计算统一以audioCtx.currentTime为基准,不要用setTimeout这类精度不足的定时器做调度,否则还是可能出现间隙

内容的提问来源于stack exchange,提问作者Luca Mastrofini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:36:51