如何用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开始播放,通过精确的时间衔接保证无缝效果。
具体步骤与代码示例
- 定义循环时间参数(单位:秒,需根据音频实际时长调整):
// 示例值,替换为你音频的实际循环点 const loopStart = 2; // 循环起始点(秒) const loopEnd = 8; // 循环结束点(秒) const introDuration = loopStart; // 起音部分时长 const loopDuration = loopEnd - loopStart; // 循环区间时长
- 实现起音播放+循环播放的逻辑:
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(); }
- 调用函数启动播放:
// 假设audioContext、audioBuffer、globalGainNode已完成初始化 playIntroAndLoop(audioContext, audioBuffer, globalGainNode);
关键注意事项
- 时间精度:提前10ms启动循环是为了抵消JavaScript事件循环的微小延迟,若仍有间隙可微调该提前量。
- 资源清理:每次播放结束后调用
disconnect()释放节点资源,避免内存泄漏。 - 参数单位:Web Audio API的时间参数均为秒,确保
loopStart和loopEnd的数值与音频实际时长匹配。
内容的提问来源于stack exchange,提问作者Basj
相关产品推荐
相关产品推荐

