如何在Node.js中将Int16Array缓冲区保存为WAV文件

5

在音频处理过程中,我向服务器发送 Int16Array 缓冲区。

var handleSuccess = function (stream) {
        globalStream = stream;
        input = context.createMediaStreamSource(stream);
        input.connect(processor);

        processor.onaudioprocess = function (e) {
            var left = e.inputBuffer.getChannelData(0);
    var left16 = convertFloat32ToInt16(left);
    socket.emit('binaryData', left16);
        };
    };

    navigator.mediaDevices.getUserMedia(constraints)
        .then(handleSuccess);

在服务器端,我正在尝试按以下方式保存文件:
client.on('start-audio', function (data) {
        stream = fs.createWriteStream('tesfile.wav');
    });

    client.on('end-audio', function (data) {
         if (stream) {
            stream.end();
         }
         stream = null;
    });

    client.on('binaryData', function (data) {
        if (stream !== null) {
            stream.write(data);
        }
    });

但是这种方法不起作用,我该如何将这个数组缓冲区保存为wav文件?

1
我需要做同样的事情... 你现在有解决方案吗? - Ranjith Kumar
我已尝试使用https://github.com/Jam3/audiobuffer-to-wav,但没有任何运气。 - Ravimallya
@Ravimallya 在存储库中有没有一个可复制的示例?这是否使用了 socket.io - Christos Lytras
是的@ChristosLytras,这里有一个git https://github.com/ravimallya/angular-node-google-voice-api。 / bin目录中有nodejs服务器代码(socket.io),音频记录器位于src / app / dashboard组件中。我正在发送left16数组缓冲区。实时转录正常工作。但是,我需要将录制的音频保存为服务器上的wav文件。 - Ravimallya
1
@Ravimallya 请检查我的答案和存储库,里面有一个简单的项目,演示了在服务器端使用 wav NPM软件包的用法。 - Christos Lytras
1个回答

6

在输出问题中,有一种尝试直接向现有文件写入数据的方法,但这是不可行的,因为WAVE文件需要一个头部,而仅仅使用createWriteStream创建一个写文件流是不能得到头部的。您可以在此处查看有关该头部格式的信息“WAVE PCM soundfile format”

有一个npm包wav可以帮助处理将数据写入服务器的整个过程。它具有FileWriter类,该类创建一个流,将正确地处理WAVE音频数据,并在流结束时写入头部。

1. 在“start-audio”事件上创建一个WAVE FileWriter流:

// Import wav package FileWriter
const WavFileWriter = require('wav').FileWriter;

...

// Global FileWriter stream.
// It won't handle multiple client connections; it's just for demonstration
let outputFileStream;

// The UUID of the filename that's being recorded
let id;

client.on('start-audio', function() {
  id = uuidv4();

  console.log(`start-audio:${id}`);

  // Create a FileWriter stream using UUID generated for filename
  outputFileStream = new WavFileWriter(`./audio/recs/${id}.wav`, {
    sampleRate: 16000,
    bitDepth: 16,
    channels: 1
  });
});

2.使用我们创建的流,在binaryData事件上编写音频数据:

client.on('binaryData', function(data) {
  console.log(`binaryData:${id}, got ${data ? data.length : 0} bytes}`);

  // Write the data directly to the WAVE file stream
  if (outputFileStream) {
    outputFileStream.write(data);
  }
});

3. 当我们接收到end-audio事件时结束流:

client.on('end-audio', function() {
  console.log(`end-audio:${id}`);

  // If there is a stream, end it.
  // This will properly handle writing WAVE file header
  if (outputFileStream) {
    outputFileStream.end();
  }

  outputFileStream = null;
});

我创建了一个 Github 存储库,其中包含此示例,您可以在此处找到:https://github.com/clytras/node-wav-stream

需要注意的是,使用类似这样的数据处理将导致问题,因为使用此代码时,每个连接的客户端只有一个 FileWriter 流变量。您应该为每个客户端流创建一个数组,并使用客户端会话 ID 来存储和识别属于相应客户端的每个流项。


非常感谢。需要进行一项小更改来结束文件流。我们需要检查 if (!outputFileStream.closed) { 而不是 if (outputFileStream) { - Ravimallya
1
我认为你不需要这样做,因为如果我们有一个流outputFileStream !== null,那么它没有被关闭,因为我们在“end-audio”事件上设置了outputFileStream = null。更安全的方法是在检查是否关闭之前先检查它是否存在,因为如果没有对象,那么检查closed将导致错误。所以最好这样做:if (outputFileStream && !outputFileStream.closed) { - Christos Lytras
是的,我赞同。 - Ravimallya
我尝试了你的解决方案,但它没有正确转换wav文件,里面没有音频。 - Vimal
1
@Vimal 在这里你可以试一试:https://github.com/ravimallya/angular-node-google-voice-api。我没有更新过代码,但这个答案肯定会对你有帮助。如果你遇到任何问题,请告诉我们。 - Ravimallya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接