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

Ama*_*pta 5 javascript websocket node.js 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)
    });
});
Run Code Online (Sandbox Code Playgroud)

在客户端

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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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

未捕获的DOMException:无法在'AudioBufferSourceNode'上设置'buffer'属性:已经设置后无法设置缓冲区

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

1va*_*dis 5

缓冲区一旦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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

我试图让代码风格尽可能接近原始风格,但现在是 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);
    })
  );
}
Run Code Online (Sandbox Code Playgroud)

如果您想在新数据到达时立即停止旧节点(看起来您希望通过重置来实现这一点,.buffer但我不确定),您必须存储它们并disconnect在需要时调用所有节点。


Ste*_*des 1

不是积极的,但我认为你必须以不同的方式处理你的流式 websocket 缓冲区。也许websocket-streaming-audio包源代码可以为您提供有关如何处理您的场景的更好线索。