通过Web Audio API进行分块/audio回放

我在最后一篇文章中提出了这个问题,但是因为这个问题与原来的问题有关,所以我将它们分开发布。 我无法通过networkingaudio播放audio,就像在媒体播放器中播放audio一样。 我已经尝试了2种不同的传输协议,binaryjs和socketio,并且在尝试通过Webaudio播放时也没有什么不同。 为了排除传输audio数据的问题,我创build了一个例子,在从客户端接收到数据后将数据发送回服务器,并将返回的数据转储到标准输出。 将其configuration到VLC中会产生您期望听到的聆听体验。

要通过vlc播放结果时听到结果,听起来应该如此, 请使用以下命令运行https://github.com/grkblood13/web-audio-stream/tree/master/vlc上的示例:

$ node webaudio_vlc_svr.js | vlc -

不pipe什么原因,当我尝试通过Webaudio播放相同的audio数据时,它失败了。 结果是随机噪声,两者之间有很大的空白。

下面的代码会导致播放声音如此糟糕?

 window.AudioContext = window.AudioContext || window.webkitAudioContext; var context = new AudioContext(); var delayTime = 0; var init = 0; var audioStack = []; client.on('stream', function(stream, meta){ stream.on('data', function(data) { context.decodeAudioData(data, function(buffer) { audioStack.push(buffer); if (audioStack.length > 10 && init == 0) { init++; playBuffer(); } }, function(err) { console.log("err(decodeAudioData): "+err); }); }); }); function playBuffer() { var buffer = audioStack.shift(); setTimeout( function() { var source = context.createBufferSource(); source.buffer = buffer; source.connect(context.destination); source.start(context.currentTime); delayTime=source.buffer.duration*1000; // Make the next buffer wait the length of the last buffer before being played playBuffer(); }, delayTime); } 

完整的源代码: https : //github.com/grkblood13/web-audio-stream/tree/master/binaryjs

你真的不能像这样调用source.start(audioContext.currentTime)。

setTimeout()有一个很长而不精确的延迟 – 其他主线程的东西可以继续,所以你的setTimeout()调用可以延迟几毫秒,甚至几十毫秒(通过垃圾收集,JS执行,布局…)代码正试图立即播放audio – 这需要在大约0.02ms的精度内启动,而不会出现故障 – 在一个具有数十毫秒不精确度的计时器上。

networkingaudio系统的重点在于audio调度程序在单独的高优先级线程中工作,并且可以以非常高的精度预先调度audio(开始,停止和audioparam更改)。 你应该重写你的系统:

1)跟踪当第一个块在audiocontext时间计划 – 不要立即安排第一个块,给一些延迟,使您的networking,希望跟上。

2)基于其“下一个块”定时来调度未来接收的每个连续块。

例如(注意我没有testing这个代码,这是我的头顶):

 window.AudioContext = window.AudioContext || window.webkitAudioContext; var context = new AudioContext(); var delayTime = 0; var init = 0; var audioStack = []; var nextTime = 0; client.on('stream', function(stream, meta){ stream.on('data', function(data) { context.decodeAudioData(data, function(buffer) { audioStack.push(buffer); if ((init!=0) || (audioStack.length > 10)) { // make sure we put at least 10 chunks in the buffer before starting init++; scheduleBuffers(); } }, function(err) { console.log("err(decodeAudioData): "+err); }); }); }); function scheduleBuffers() { while ( audioStack.length) { var buffer = audioStack.shift(); var source = context.createBufferSource(); source.buffer = buffer; source.connect(context.destination); if (nextTime == 0) nextTime = context.currentTime + 0.05; /// add 50ms latency to work well across systems - tune this if you like source.start(nextTime); nextTime+=source.buffer.duration; // Make the next buffer wait the length of the last buffer before being played }; }