red*_* 87 5 javascript audio web-audio-api
我正在尝试找出通过Web Audio API播放同步音频轨道的最佳方法是什么。我正在尝试实现的是一次播放多个.wav文件,同时尽可能减少音轨同步中的延迟。
我发现同时播放多个音轨的唯一方法是创建多个音轨并在for循环中循环播放。这样做的问题是循环之间只有极短的延迟。延迟通常只有几毫秒,这取决于用户的机器,但是当我有30条需要同时开始的音轨,而我的循环必须循环遍历30条音轨并调用source.start()
它们时,会有一个循环开始第30条音轨时出现明显的延迟。
因为我需要尽可能按时播放曲目,所以我想知道是否还有其他解决方案。例如,也许可以在其中通过Web Audio API加载多个源,然后产生一个本地全局事件,该事件将同时启动所有这些轨道。
这是一些显示问题的代码:
const audioBuffer1 = '...'; // Some decoded audio buffer
const audioBuffer2 = '...'; // some other decoded audio buffer
const audioBuffer3 = '...'; // and another audio buffer
const arrayOfAudioBuffers = [audioBuffer1, audioBuffer2, audioBuffer3];
const context = new AudioContext();
function play(audioBuffer) {
const source = context.createBufferSource();
source.buffer = audioBuffer;
source.connect(context.destination);
source.start();
}
for (let i = 0; i < arrayOfAudioBuffers.length; i++) {
// every time this loops the play function is
// called around 2 milliseconds after the previous
// one causing sounds to get slightly out of sync
play(arrayOfAudioBuffers[i]);
}
Run Code Online (Sandbox Code Playgroud)
Splice Beatmaker是使用多个轨道源并设法保持良好同步的应用示例。我已经探索了一些库,例如Howler和Tone,但它们似乎使用了我相信的循环方法。
很想听听有关如何解决此问题的任何建议
由于浏览器可以防止指纹识别和计时攻击,现代浏览器可能会降低或调整引擎盖下的计时精度。这意味着source.start(offset)
在您的情况下永远不可能 100% 准确或可靠。我推荐的是逐字节混合源,然后回放最终混合。假设所有音频源应同时启动,并且加载时间灵活,以下将起作用:
例子:
const audioBuffer1 = '...'; // Some decoded audio buffer
const audioBuffer2 = '...'; // some other decoded audio buffer
const audioBuffer3 = '...'; // and another audio buffer
const arrayOfAudioBuffers = [audioBuffer1, audioBuffer2, audioBuffer3];
Run Code Online (Sandbox Code Playgroud)
我们需要通过获取最大长度的缓冲区来计算整首歌曲的长度。
let songLength = 0;
for(let track of arrayOfAudioBuffers){
if(track.length > songLength){
songLength = track.length;
}
}
Run Code Online (Sandbox Code Playgroud)
接下来我创建了一个方法来接收arrayOfAudioBuffers
和输出最终的混音。
function mixDown(bufferList, totalLength, numberOfChannels = 2){
//create a buffer using the totalLength and sampleRate of the first buffer node
let finalMix = context.createBuffer(numberOfChannels, totalLength, bufferList[0].sampleRate);
//first loop for buffer list
for(let i = 0; i < bufferList.length; i++){
// second loop for each channel ie. left and right
for(let channel = 0; channel < numberOfChannels; channel++){
//here we get a reference to the final mix buffer data
let buffer = finalMix.getChannelData(channel);
//last is loop for updating/summing the track buffer with the final mix buffer
for(let j = 0; j < bufferList[i].length; j++){
buffer[j] += bufferList[i].getChannelData(channel)[j];
}
}
}
return finalMix;
}
Run Code Online (Sandbox Code Playgroud)
仅供参考:您始终可以通过对每个通道的更新进行硬编码来删除一个循环。
现在我们可以mixDown
像这样使用我们的函数:
const mix = context.createBufferSource();
//call our function here
mix.buffer = mixDown(arrayOfAudioBuffers, songLength, 2);
mix.connect(context.destination);
//will playback the entire mixdown
mix.start()
Run Code Online (Sandbox Code Playgroud)
在此处详细了解网络音频精确计时
注意:我们可以OfflineAudioContext
用来完成同样的事情,但不能保证精度,仍然依赖于循环和调用start()
每个单独的源。
希望这可以帮助。
归档时间: |
|
查看次数: |
221 次 |
最近记录: |