68
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

getUserMediaで録音したデータをWAVファイルとして保存する

1. 録音する

以下のように作った関数 startRecording を呼ぶと
変数 audioData に録音した音声データが取得できます。

        var localMediaStream = null;
        var localScriptProcessor = null;
        audioData = []; // 録音データ

        var onAudioProcess = function(e) {
          var input = e.inputBuffer.getChannelData(0);
          var bufferData = new Float32Array(bufferSize);
          for (var i = 0; i < bufferSize; i++) {
            bufferData[i] = input[i];
          }

          audioData.push(bufferData);
        };
        var startRecording = function() {
          navigator.getUserMedia(
            { audio: true },
            function(stream) {
              localMediaStream = stream;
              var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
              localScriptProcessor = scriptProcessor;
              var mediastreamsource = audioContext.createMediaStreamSource(stream);
              mediastreamsource.connect(scriptProcessor);
              scriptProcessor.onaudioprocess = onAudioProcess;
              scriptProcessor.connect(audioContext.destination);
            },
            function(e) {
              console.log(e);
            }
          );
        };

2. WAVファイルに変換する

以下のように作った関数 exportWAV に 1 で作成した audioData を引数にわたして呼び出すと
WAVファイルを作成して、その url を返却してくれます。

        var exportWAV = function(audioData) {

          var encodeWAV = function(samples, sampleRate) {
            var buffer = new ArrayBuffer(44 + samples.length * 2);
            var view = new DataView(buffer);

            var writeString = function(view, offset, string) {
              for (var i = 0; i < string.length; i++){
                view.setUint8(offset + i, string.charCodeAt(i));
              }
            };

            var floatTo16BitPCM = function(output, offset, input) {
              for (var i = 0; i < input.length; i++, offset += 2){
                var s = Math.max(-1, Math.min(1, input[i]));
                output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
              }
            };

            writeString(view, 0, 'RIFF');  // RIFFヘッダ
            view.setUint32(4, 32 + samples.length * 2, true); // これ以降のファイルサイズ
            writeString(view, 8, 'WAVE'); // WAVEヘッダ
            writeString(view, 12, 'fmt '); // fmtチャンク
            view.setUint32(16, 16, true); // fmtチャンクのバイト数
            view.setUint16(20, 1, true); // フォーマットID
            view.setUint16(22, 1, true); // チャンネル数
            view.setUint32(24, sampleRate, true); // サンプリングレート
            view.setUint32(28, sampleRate * 2, true); // データ速度
            view.setUint16(32, 2, true); // ブロックサイズ
            view.setUint16(34, 16, true); // サンプルあたりのビット数
            writeString(view, 36, 'data'); // dataチャンク
            view.setUint32(40, samples.length * 2, true); // 波形データのバイト数
            floatTo16BitPCM(view, 44, samples); // 波形データ

            return view;
          };

          var mergeBuffers = function(audioData) {
            var sampleLength = 0;
            for (var i = 0; i < audioData.length; i++) {
              sampleLength += audioData[i].length;
            }
            var samples = new Float32Array(sampleLength);
            var sampleIdx = 0;
            for (var i = 0; i < audioData.length; i++) {
              for (var j = 0; j < audioData[i].length; j++) {
                samples[sampleIdx] = audioData[i][j];
                sampleIdx++;
              }
            }
            return samples;
          };

          var dataview = encodeWAV(mergeBuffers(audioData), audioContext.sampleRate);
          var audioBlob = new Blob([dataview], { type: 'audio/wav' });

          var myURL = window.URL || window.webkitURL;
          var url = myURL.createObjectURL(audioBlob);
          return url;
        };

参考サイト

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
68
Help us understand the problem. What are the problem?