Playing PCM stream from Web Audio API on Node.js

Con*_*ick 18 audio stream pcm node.js web-audio-api

I'm streaming recorded PCM audio from a browser with web audio api.

I'm streaming it with binaryJS (websocket connection) to a nodejs server and I'm trying to play that stream on the server using the speaker npm module.

This is my client. The audio buffers are at first non-interleaved IEEE 32-bit linear PCM with a nominal range between -1 and +1. I take one of the two PCM channels to start off and stream it below.

var client = new BinaryClient('ws://localhost:9000');
var Stream = client.send();

recorder.onaudioprocess = function(AudioBuffer){
    var leftChannel = AudioBuffer.inputBuffer.getChannelData (0);
    Stream.write(leftChannel);
}
Run Code Online (Sandbox Code Playgroud)

Now I receive the data as a buffer and try writing it to a speaker object from the npm package.

var Speaker = require('speaker');

var speaker = new Speaker({
  channels: 1,          // 1 channel
  bitDepth: 32,         // 32-bit samples
  sampleRate: 48000,     // 48,000 Hz sample rate
  signed:true
});

server.on('connection', function(client){
    client.on('stream', function(stream, meta){
        stream.on('data', function(data){
            speaker.write(leftchannel);
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

The result is a high pitch screech on my laptop's speakers, which is clearly not what's being recorded. It's not feedback either. I can confirm that the recording buffers on the client are valid since I tried writing them to a WAV file and it played back fine.

The docs for speaker and the docs for the AudioBuffer in question

I've been stumped on this for days. Can someone figure out what is wrong or perhaps offer a different approach?

Update with solution

First off, I was using the websocket API incorrectly. I updated above to use it correctly.

I needed to convert the audio buffers to an array buffer of integers. I choose to use Int16Array. Since the given audio buffer has a range in-between 1 and -1, it was as simple as multiplying by the range of the new ArrayBuffer (32767 to -32768).

recorder.onaudioprocess = function(AudioBuffer){

    var left = AudioBuffer.inputBuffer.getChannelData (0);

    var l = left.length;
    var buf = new Int16Array(l)

    while (l--) {
        buf[l] = left[l]*0xFFFF;    //convert to 16 bit
    }

    Stream.write(buf.buffer);

}
Run Code Online (Sandbox Code Playgroud)

gra*_*ant 1

看起来您正在将流作为meta对象发送。

根据文档,按顺序BinaryClient.send获取一个data对象(流)和一个对象。meta事件的回调接收第一个参数中的stream流(作为BinaryStream对象,而不是)和第二个参数中的对象。Buffermeta

您将send()字符串'channel'作为流传递,并将Float32ArrayfromgetChannelData()作为元对象传递。也许如果您要交换这两个参数(或仅使用client.send(leftChannel)),然后更改要传递的服务器代码而不是(可能应该重命名为stream,或者如果不需要则删除),它可能会起作用。speaker.writeleftchannelmeta

请注意,由于Float32Array不是流或缓冲区对象,BinaryJS 可能会尝试将其以一个块的形式发送。您可能想发送leftChannel.bufferArrayBuffer对象后面的)。

让我知道这是否适合您;我现在无法测试您的确切设置。