azhar
azhar

Reputation: 1747

How to save Int16Array buffer to wav file node js

I am sending Int16Array buffer to server while audio processing

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

and in server i am trying to save the file as follows

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

But this is not working so how i save this array buffer as wav file?

Upvotes: 5

Views: 4562

Answers (1)

Christos Lytras
Christos Lytras

Reputation: 37308

At the O/P question, there is an attempt to write and add data directly to an existing file which it can't work because WAVE files need to have a header and there can't be a header by just creating a write file stream using createWriteStream. You can check about that header format here "WAVE PCM soundfile format".

There is the wav NPM package which it can help to handle the whole process of writing the data to the server. It has the FileWriter class which creates a stream that will properly handle WAVE audio data and it will write the header when the stream ends.

1. Create a WAVE FileWriter stream on start-audio event:

// 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. Use the stream we created to write audio data on binaryData event:

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 the stream when we receive end-audio event:

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

I have created a Github repository with this example which you can find here: https://github.com/clytras/node-wav-stream.

Keep in mind that handling data like this will result in issues because using this code, there is only one FileWriter stream variable for every client that connects. You should create an array for each client stream and use client session ID's to store and identify each stream item that belongs to the corresponding client.

Upvotes: 6

Related Questions