如何使用来自web-socket的Web音频API来传输音频块?

时间:2016-12-27 14:07:59

标签: javascript node.js websocket web-audio-api

我通过来自服务器的web-Socket以块的形式传输音频数据

ws.on('message', function incoming(message) {
    var readStream = fs.createReadStream("angular/data/google.mp3",
        {
            'flags': 'r',
            'highWaterMark': 128 * 1024
        }
    );
    readStream.on('data', function(data) {
        ws.send(data);
    });

    readStream.on('end', function() {
        ws.send('end');
    });

    readStream.on('error', function(err) {
        console.log(err)
    });
});

在客户端

var chunks = [];
var context = new AudioContext();
var soundSource;

var ws = new WebSocket(url);
    ws.binaryType = "arraybuffer";

ws.onmessage = function(message) {
    if (message.data instanceof ArrayBuffer) {
        chunks.push(message.data)
    } else {
        createSoundSource(chunks);
    }
};

function createSoundSource(audioData) {
    soundSource = context.createBufferSource();

    for (var i=0; i < audioData.length;i++) {
        context.decodeAudioData(audioData[i], function(soundBuffer){
            soundSource.buffer = soundBuffer;
            soundSource.connect(context.destination);
            soundSource.start(0);
        });
    }
}

但是第二次设置缓冲区soundSource.buffer = soundBuffer;导致错误

  

未捕获的DOMException:无法设置&#39;缓冲区&#39; &#39; AudioBufferSourceNode&#39;:无法在设置缓冲区后设置缓冲区

对于如何最好地使用新音频数据进行update网络音频API播放的任何建议或见解将不胜感激。

2 个答案:

答案 0 :(得分:1)

一旦设置了AudioBufferSourceNode,就无法重置缓冲区。就像一劳永逸。每次您要播放不同的缓冲区时,都必须创建一个新的AudioBufferSourceNode才能继续播放。这些节点非常轻巧,因此即使创建大量节点也不必担心性能。

为此,您可以修改createSoundSource函数,以简单地为循环体内的每个块创建一个AudioBufferSourceNode,如下所示:

function createSoundSource(audioData) {
    for (var i=0; i < audioData.length;i++) {
        context.decodeAudioData(audioData[i], function(soundBuffer){
            var soundSource = context.createBufferSource();
            soundSource.buffer = soundBuffer;
            soundSource.connect(context.destination);
            soundSource.start(0);
        });
    }
}

我试图使代码样式尽可能接近原始样式,但现在是2020年,利用现代功能的功能实际上看起来像这样:

async function createSoundSource(audioData) {
  await Promise.all(
    audioData.map(async (chunk) => {
      const soundBuffer = await context.decodeAudioData(chunk);
      const soundSource = context.createBufferSource();
      soundSource.buffer = soundBuffer;
      soundSource.connect(context.destination);
      soundSource.start(0);
    })
  );
}

如果您想在新数据到达后立即停止旧节点(通过重置.buffer来实现这一点,但我不确定),则必须存储它们并调用{ {1}}在所有时候都开始。

答案 1 :(得分:0)

不是肯定的,但是我认为您在处理流式websocket缓冲区时必须有所不同。也许websocket-streaming-audio软件包的源代码可以为您提供有关如何处理方案的更好的线索。