通过Web Audio API与分块音频进行混乱/听不清的播放

时间:2013-12-09 16:49:19

标签: html5 node.js audio-streaming web-audio

我在上一篇文章中提到了这一点,但由于它是原始问题的主题,我将单独发布。我无法通过Web Audio播放传输的音频,就像在媒体播放器中播放一样。我尝试过两种不同的传输协议,binaryjs和socketio,在尝试播放Web Audio时都没有区别。为了排除音频数据的传输问题,我创建了一个示例,它在从客户端收到数据并将返回转储到stdout后将数据发送回服务器。将其导入VLC会产生您期望听到的聆听体验。

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

$ node webaudio_vlc_svr.js | vlc -

无论出于何种原因,当我尝试通过Web 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

1 个答案:

答案 0 :(得分:8)

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

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

网络音频系统的重点是音频调度程序在一个单独的高优先级线程中工作,您可以非常高的准确度预先安排音频(开始,停止和音频更改)。您应该将系统重写为:

1)跟踪第一个块是在audiocontext时间安排的时间 - 并且DON&#T; T立即安排第一个块,给出一些延迟,以便您的网络可以跟上。

2)根据"下一个块"计划将来收到的每个连续块。定时。

e.g。 (注意我还没有测试过这段代码,这不是我的头脑):

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
    };
}