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

如何用Web Audio API实现音频文件在两点间无缝循环

使用Web Audio API实现指定区间的无缝音频循环

我已经掌握如何通过Web Audio API从数组缓冲区(来自RAW 16位音频数据,如WAV文件)播放音频,基础代码如下:

const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
gainNode = audioContext.createGain();
gainNode.gain.value = 1;
source.connect(gainNode);
gainNode.connect(globalGainNode);
source.start();

现在需要实现音频在两个循环点之间的无缝循环,播放逻辑如下:

  • 先播放从0到loop_start的起音部分(仅播放一次)
  • 无缝循环播放loop_start到loop_end的区间

播放流程示意图:

0                 loop_start                loop_end
|-----------------|-------------------------|
                  [========= LOOP ==========]

注:此需求并非普通JavaScript音频循环问题的重复,因为<audio>标签的loop属性无法满足:1. 未使用<audio>标签,而是采用WebAudioAPI缓冲区;2. loop属性是从t=0开始循环,而需求是从指定的loop_start开始循环。


实现方案

核心思路是监听音频播放进度,在每次播放到loop_end时,立刻创建新的BufferSource节点从loop_start开始播放,通过精确的时间衔接保证无缝效果。

具体步骤与代码示例

  1. 定义循环时间参数(单位:秒,需根据音频实际时长调整):
// 示例值,替换为你音频的实际循环点
const loopStart = 2; // 循环起始点(秒)
const loopEnd = 8;   // 循环结束点(秒)
const introDuration = loopStart; // 起音部分时长
const loopDuration = loopEnd - loopStart; // 循环区间时长
  1. 实现起音播放+循环播放的逻辑:
function playIntroAndLoop(audioContext, audioBuffer, globalGainNode) {
  // 播放起音段(0到loopStart)
  const introSource = audioContext.createBufferSource();
  introSource.buffer = audioBuffer;
  const gainNode = audioContext.createGain();
  gainNode.gain.value = 1;
  introSource.connect(gainNode);
  gainNode.connect(globalGainNode);
  
  introSource.start(audioContext.currentTime, 0, introDuration);
  
  // 提前10ms启动循环,抵消JS事件循环延迟,保证无缝衔接
  setTimeout(() => {
    startLoop(audioContext, audioBuffer, globalGainNode, loopStart, loopDuration);
  }, introDuration * 1000 - 10);
  
  // 起音段结束后清理资源
  introSource.onended = () => {
    introSource.disconnect();
  };
}

// 循环播放指定区间的递归函数
function startLoop(audioContext, audioBuffer, globalGainNode, loopStart, loopDuration) {
  function playLoopSegment() {
    const loopSource = audioContext.createBufferSource();
    loopSource.buffer = audioBuffer;
    const gainNode = audioContext.createGain();
    gainNode.gain.value = 1;
    loopSource.connect(gainNode);
    gainNode.connect(globalGainNode);
    
    // 播放当前循环段
    loopSource.start(audioContext.currentTime, loopStart, loopDuration);
    
    // 当前段结束后立刻启动下一次循环
    loopSource.onended = () => {
      loopSource.disconnect();
      playLoopSegment();
    };
  }
  
  // 启动第一次循环
  playLoopSegment();
}
  1. 调用函数启动播放:
// 假设audioContext、audioBuffer、globalGainNode已完成初始化
playIntroAndLoop(audioContext, audioBuffer, globalGainNode);

关键注意事项

  • 时间精度:提前10ms启动循环是为了抵消JavaScript事件循环的微小延迟,若仍有间隙可微调该提前量。
  • 资源清理:每次播放结束后调用disconnect()释放节点资源,避免内存泄漏。
  • 参数单位:Web Audio API的时间参数均为秒,确保loopStart和loopEnd的数值与音频实际时长匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 21:25:21