LoginSignup
16

More than 5 years have passed since last update.

Node.jsでのWAVEエンコード&保存

Last updated at Posted at 2018-05-18

はじめに

クライアント側ではなくサーバ側でgetUserMediaを保存したい!

サンプルソースはこちら
https://github.com/sinkunvv/qiita-samples/tree/master/Nodejs-wav

今回の実装で使用したライブラリは以下の通り

package.json

{
  "name": "wav-encode",
  "version": "1.0.0",
  "dependencies": {
    "express": "^3.3.8",
    "socket.io": "^2.1.0",
    "wav-encoder": "^1.3.0"
  }
}

1. getUserMediaを使用して音声データを取得する

まずはクライアント側の実装例
録音ボタン等にrecord()を紐づけてgetUserMediaで音声データを取得する

client.js

let buffer = 2048;
let context;
let processor;
let input;
let globalStream;
var audioData = [];
const property = {
    audio: true,
    video: false
};

function record() {
    context = new AudioContext();
    processor = context.createScriptProcessor(buffer, 1, 1);
    processor.connect(context.destination);
    context.resume();

    var recording = function(stream) {
        globalStream = stream;
        input = context.createMediaStreamSource(stream);
        input.connect(processor);
        processor.onaudioprocess = function(audio) {
            audioStream(audio);
        };
    };
    navigator.mediaDevices.getUserMedia(property).then(recording);
}

function audioStream(audio) {
    var input = audio.inputBuffer.getChannelData(0);
    var bufferData = new Float32Array(buffer);

    for (var i = 0; i < buffer; i++) {
        bufferData[i] = input[i];
    }
    audioData.push(bufferData);
}

2. サーバに音声データを送信する

今回はストリーミングで音声認識をする必要があったためsocket.ioを使用していた。
bufferデータを送ることができれば他の方法を使用しても良いはず。(未確認)

client.js
function stop() {
    startButton.disabled = false;
    stopButton.disabled = true;
    socket.emit('stop', audioData);
    let track = globalStream.getTracks()[0];
    track.stop();
    audioData = [];
    input.disconnect(processor);
    processor.disconnect(context.destination);
    context.close().then(function() {
        input = null;
        processor = null;
        context = null;
        startButton.disabled = false;
    });
}

録音停止したタイミングで蓄積した音声データを送る。

3.Node.jsで音声データを受け取り、WAVにエンコードする

socket.ioでstopイベントを受け取ったらそのデータをexportWAVに送る

server.js
    // 録音終了
    socket.on('stop', function(data) {
        exportWAV(data, sampleRateHertz);
        console.log('stop record');
    });
server.js

const WavEncoder = require('wav-encoder');
const fs = require('fs');

// jsonの要素数
function Objlen(data) {
    return Object.keys(data).length;
}

// 
function exportWAV(data, sampleRate) {
    var mergeBuffers = function(data) {
        var sampleLength = 0;
        for(var i = 0; i < data.length; i++) {
            sampleLength += Objlen(data[i]);
        }

        var samples = new Float32Array(sampleLength);
        var sampleIndex = 0;

        for(var i = 0; i < data.length; i++) {
            for(var j = 0; j < Objlen(data[i]); j++) {
                samples[sampleIndex] = data[i][j]
                sampleIndex++;
            }
        }
        return samples;
    };

    var audioData = {
            sampleRate: 44100,
            channelData: [mergeBuffers(data)]
        };

    WavEncoder.encode(audioData).then((buffer) => {
        fs.writeFile('demo.wav', Buffer.from(buffer), function(e) {
            if (e) {
                console.log(e)
            } else {
                console.log("Success")
            }
        });
    });
}

終わり

音声認識での機械学習用にデータを収集したかったが
クライアント側でwavに変換してダウンロードする的なのは多くまとめられていたけれど
サーバ側で保存するが中々見つからなかったのでまとめてみた。
wav-encoderには助けられた...

誰かの足がかりになればと。

参考サイト

getUserMediaで録音したデータをWAVファイルとして保存する - Qiita
Media Capture and Streams と Web Audio API で実現する録画・録音・ WAVファイルの生成 | CYOKODOG
javascript - Node.js can´t create Blobs? - Stack Overflow

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16