web-dev-qa-db-ja.com

ユーザーからの音声を録音してサーバーに保存する

Webサイトのユーザーからの音声を録音し、サーバーに保存しようとしています。私がこれまでに研究した投稿の多くは、MattDiamondのrecorderjsを参照しています。ブラウザからソースコードを開いて、 http://webaudiodemos.appspot.com/AudioRecorder/index.html でデモを再作成しようとしました。 html、「audiodisplay.js」、「recorder.js」、「main.js」をコピーしてサーバーに配置しました。また、彼のGitHubサイトから「recorderWorker.js」ファイルを追加しました。 recorder.jsファイルで、var WORKER_PATH = 'js/recorderjs /recorderWorker.js'をvarWORKER_PATH = 'recorderWorker.js'に変更しました。

設定したデモを実行すると、「マイクを共有しますか?」という警告が表示され、右側のマイクアイコンを押すと録音を開始できます。ただし、録音を停止すると、マットのデモのようにオーディオ波形が下に表示されず、保存アイコンがアクティブになりません。

デモを起動して実行できる場合、次の問題は、デモのようにローカルではなく、wavファイルをサーバーに保存することです。 XMLHttpRequest()を使用するように言っている投稿をいくつか見つけましたが、それらの例をrecorderjsに接続する方法を実際に理解することはできません。 WAVファイルの記録をサーバーに保存ChromeサーバーにHTML5&getUserMedia-オーディオを録音して一定時間後にWebサーバーに保存RecorderJS記録されたblobをAJAX経由でアップロード

7
user3080392

私は1つの解決策を見つけましたが、recorderjsに関連する他の解決策を歓迎します。 https://github.com/icatcher-at/MP3RecorderJS でMP3RecorderJSを使用しました。デモhtmlは、htmlの先頭をsrc = "js /jquery.min.js"およびsrc = "js /mp3recorder.js"からサーバー内の任意の場所に変更すると機能します。私にとっては、src = "jquery.min.js"とsrc = "mp3recorder.js"です。「mp3recorder.js」ファイルに対しても同じことを行う必要がありました。varRECORDER_WORKER_PATH= 'js/recorderWorker.js'; var ENCODER_WORKER_PATH = 'js/mp3Worker.js'; var RECORDER_WORKER_PATH = 'recorderWorker.js'に変更されました。 var ENCODER_WORKER_PATH = 'mp3Worker.js';

プログラムは、mp3とwavの両方を録音するように設定されています。 wavが欲しかったので、htmlファイルにさらにいくつかの調整を加えました。 55行目には次のようなものがあります。

recorderObject.exportMP3(function(base64_mp3_data) {
      var url = 'data:audio/mp3;base64,' + base64_mp3_data;
      var au  = document.createElement('audio'); 

私はそれを次のように変更しました:

recorderObject.exportWAV(function(base64_wav_data) {
      var url = 'data:audio/wav;base64,' + base64_wav_data;
      var au  = document.createElement('audio');   

デモでは、録音するたびに新しいプレーヤーが追加されます。これを防ぐために、$ recorder.append(au);を削除(コメントアウト)しました。一部、オーディオプレーヤーを格納するための新しいdivを作成し、オーディオプレーヤーが作成される前に、毎回そのdivをクリアします。サーバーにアップロードするために、サーバーに画像をアップロードすることから学んだテクニックを使用しました キャンバス画像をサーバーに保存 基本的に、56行目の「url」変数は必要なものでしたが、理解できませんでした別の関数で使用するためにそれをユニバーサル変数に入れる方法を説明します。そこで、非表示のdivを作成し、その内容を「url」と等しくしました。次に、「upload」という新しい関数でそのdivを参照しました。次に、「uploadWav.php」というphpファイルを使用しました。ユーザーが記録する前に空のファイルをアップロードできないように、アップロードボタンをアクティブまたは非アクティブにする方法を見つける必要がありますが、それは別の問題です。これが私のために働いた最終的なhtmlとphpです:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>MP3 Recorder test</title>
  </head>
  <body id="index" onload="">

    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src="mp3recorder.js"></script>
    <script type="text/javascript">
    var audio_context;

    function __log(e, data) {
      log.innerHTML += "\n" + e + " " + (data || '');
    }

    $(function() {

      try {
    // webkit shim
    window.AudioContext = window.AudioContext || window.webkitAudioContext;
    navigator.getUserMedia = ( navigator.getUserMedia ||
                     navigator.webkitGetUserMedia ||
                     navigator.mozGetUserMedia ||
                     navigator.msGetUserMedia);
    window.URL = window.URL || window.webkitURL;

    var audio_context = new AudioContext;
    __log('Audio context set up.');
    __log('navigator.getUserMedia ' + (navigator.getUserMedia ? 'available.' : 'not  present!'));
  } catch (e) {
    alert('No web audio support in this browser!');
  }

  $('.recorder .start').on('click', function() {
    $this = $(this);
    $recorder = $this.parent();

    navigator.getUserMedia({audio: true}, function(stream) {
      var recorderObject = new MP3Recorder(audio_context, stream, { statusContainer:  $recorder.find('.status'), statusMethod: 'replace' });
      $recorder.data('recorderObject', recorderObject);

      recorderObject.start();
    }, function(e) { });
  });

  $('.recorder .stop').on('click', function() {
    $this = $(this);
    $recorder = $this.parent();

    recorderObject = $recorder.data('recorderObject');
    recorderObject.stop();

    recorderObject.exportWAV(function(base64_wav_data) {
      var url = 'data:audio/wav;base64,' + base64_wav_data;
      var au  = document.createElement('audio');

      document.getElementById("playerContainer").innerHTML = "";
      //console.log(url)

      var duc = document.getElementById("dataUrlcontainer");
      duc.innerHTML = url;

      au.controls = true;
      au.src = url;
      //$recorder.append(au);
      $('#playerContainer').append(au);

      recorderObject.logStatus('');
    });

  });

  });
  </script>


 <script>
    function upload(){

    var dataURL = document.getElementById("dataUrlcontainer").innerHTML;

      $.ajax({
      type: "POST",
      url: "uploadWav.php",
      data: { 
          wavBase64: dataURL
       }
    }).done(function(o) {
      console.log('saved'); 

      });

    }    
  </script>


 <div class="recorder">
  Recorder 1
  <input type="button" class="start"  value="Record" />
  <input type="button" class="stop" value="Stop" />
  <pre class="status"></pre>
 </div>

 <div><button onclick="upload()">Upload</button></div>

 <div id="playerContainer"></div>

 <div id="dataUrlcontainer" hidden></div>

 <pre id="log"></pre>

 </body>
 </html>

および「uploadWav.php」ファイル:

<?php
// requires php5
define('UPLOAD_DIR', 'uploads/');
$img = $_POST['wavBase64'];
$img = str_replace('data:audio/wav;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$data = base64_decode($img);
$file = UPLOAD_DIR . uniqid() . '.wav';
$success = file_put_contents($file, $data);
print $success ? $file : 'Unable to save the file.';
?> 
6
user3080392

XMLHttpRequestを使用してwavまたはmp3blobをサーバーに投稿するのは簡単です。

blob要素にアクセスできる場所ならどこでも、このコードを実行するだけです。

_var xhr=new XMLHttpRequest();
xhr.onload=function(e) {
  if(this.readyState === 4) {
      console.log("Server returned: ",e.target.responseText);
  }
};
var fd=new FormData();
fd.append("audio_data",blob, "filename");
xhr.open("POST","upload.php",true);
xhr.send(fd);
_

JQueryを必要としないため、$.ajax()よりもXMLHttpRequestの方が好きです。

サーバー側の_upload.php_は次のように単純です。

_$input = $_FILES['audio_data']['tmp_name']; //temporary name that PHP gave to the uploaded file
$output = $_FILES['audio_data']['name'].".wav"; //letting the client control the filename is a rather bad idea

//move the file from temp name to local folder using $output name
move_uploaded_file($input, $output)
_

出典: https://addpipe.com/blog/using-recorder-js-to-capture-wav-audio-in-your-html5-web-site/ ライブデモ: https://addpipe.com/simple-recorderjs-demo/

6
Octavian Naicu
//**Server Side Code**
package myPack;

import Java.io.File;
import Java.io.FileOutputStream;
import Java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.Apache.commons.codec.binary.Base64;

@WebServlet("/MyServlet")
@MultipartConfig
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public MyServlet() {
        super();
    }

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        try {
            String name = request.getParameter("fname");
            String url = request.getParameter("myUrl");
            url = url.replace("data:audio/wav;base64,", "");
            url = url.replace(" ", "+");
            byte[] bytes = url.getBytes();
            byte[] valueDecoded = Base64.decodeBase64(bytes);
            FileOutputStream os = new FileOutputStream(new File("D://" + name
                    + ".wav"));
            os.write(valueDecoded);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

**Client Side Code**


<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>MP3 Recorder test</title>
</head>
<body id="index" onload="">

    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/recorder.js"></script>
    <script type="text/javascript">
    var audio_context;

    function __log(e, data) {
      log.innerHTML += "\n" + e + " " + (data || '');
    }

    $(function() {

      try {
        // webkit shim
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        navigator.getUserMedia = ( navigator.getUserMedia ||
                         navigator.webkitGetUserMedia ||
                         navigator.mozGetUserMedia ||
                         navigator.msGetUserMedia);
        window.URL = window.URL || window.webkitURL;

        var audio_context = new AudioContext;
        __log('Audio context set up.');
        __log('navigator.getUserMedia ' + (navigator.getUserMedia ? 'available.' : 'not present!'));
      } catch (e) {
        alert('No web audio support in this browser!');
      }

      $('.recorder .start').on('click', function() {
        $this = $(this);
        $recorder = $this.parent();

        navigator.getUserMedia({audio: true}, function(stream) {
          var recorderObject = new MP3Recorder(audio_context, stream, { statusContainer: $recorder.find('.status'), statusMethod: 'replace' });
          $recorder.data('recorderObject', recorderObject);

          recorderObject.start();
        }, function(e) { });
      });

      $('.recorder .stop').on('click', function() {
        $this = $(this);
        $recorder = $this.parent();

        recorderObject = $recorder.data('recorderObject');
        recorderObject.stop();

        recorderObject.exportWAV(function(base64_wav_data) {
            var url = 'data:audio/wav;base64,' + base64_wav_data;
            var au  = document.createElement('audio'); 
            document.getElementById("playerContainer").innerHTML = "";
            //console.log(url)

            var duc = document.getElementById("dataUrlcontainer");
            duc.innerHTML = url;

            au.controls = true;
            au.src = url;
            //$recorder.append(au);
            $('#playerContainer').append(au);

            var fd = new FormData();
            fd.append('fname', 'test.wav');
            fd.append('myUrl', duc.innerHTML);
         $.ajax({
              type: "POST",
              url: "/audioPart2/MyServlet",
              data: fd,
              processData: false,
              contentType: false
            });
          recorderObject.logStatus('');
        });

      });

    });
    </script>

    <div class="recorder">
        Recorder 1 <input type="button" class="start" value="Record" /> <input
            type="button" class="stop" value="Stop" />
        <div id="playerContainer"></div>
        <div id="dataUrlcontainer" hidden></div>
        <pre class="status"></pre>
    </div>

<!--    <div class="recorder"> -->
<!--        Recorder 2 <input type="button" class="start" value="Record" /> <input -->
<!--            type="button" class="stop" value="Stop" /> -->
<!--        <pre class="status"></pre> -->
<!--    </div> -->

    <pre id="log"></pre>
</body>
</html>

**// Required JS 
1)jquery.min.js
2) recorder.js**


**recorder.js is below**
(function(window){

  var RECORDER_WORKER_PATH  = 'js/recorderWorker.js';
  var ENCODER_WORKER_PATH   = 'js/mp3Worker.js';


  var MP3Recorder = function(context, stream, cfg) {
    var config      = cfg || { statusContainer: null, statusMethod: 'append' }

    var bufferLen   = 4096;
    var recording   = false;

    this.source     = context.createMediaStreamSource(stream);
    this.node       = (context.createScriptProcessor || context.createJavaScriptNode).call(context, bufferLen, 1, 1);

    var recorderWorker  = new Worker(RECORDER_WORKER_PATH);
    var encoderWorker   = new Worker(ENCODER_WORKER_PATH);
    var exportCallback;


    // initialize the Recorder Worker
    recorderWorker.postMessage({ cmd: 'init', sampleRate: context.sampleRate });

    // the recording loop
    this.node.onaudioprocess = function(e) {
      if(!recording) return;
      recorderWorker.postMessage({ cmd: 'record', buffer: e.inputBuffer.getChannelData(0) });
    }


    this.start = function() {
      recording = true;
      this.logStatus('recording...');
    }
    this.stop = function() {
      recording = false;
      this.logStatus('stopping...');
    }
    this.destroy  = function() { recorderWorker.postMessage({ cmd: 'destroy' }); }

    this.logStatus = function(status) {
      if(config.statusContainer) {
        if(config.statusMethod == 'append') {
          config.statusContainer.text(config.statusContainer.text + "\n" + status);
        } else {
          config.statusContainer.text(status);
        }
      }
    }

    this.exportBlob = function(cb) {
      exportCallback = cb;
      if (!exportCallback) throw new Error('Callback not set');
      recorderWorker.postMessage({ cmd: 'exportBlob' });
    }

    this.exportWAV = function(cb) {
      // export the blob from the worker
      this.exportBlob(function(blob) {
        var fileReader = new FileReader();

        // read the blob as array buffer and convert it
        // to a base64 encoded WAV buffer
        fileReader.addEventListener("loadend", function() {
          var resultBuffer = new Uint8Array(this.result);
          cb(encode64(resultBuffer));
        });
        fileReader.readAsArrayBuffer(blob);
      });
    }

    this.exportMP3 = function(cb) {
      this.logStatus('converting...');

      // export the blob from the worker
      this.exportBlob(function(blob) {
        var fileReader = new FileReader();

        fileReader.addEventListener("loadend", function() {
          var wavBuffer = new Uint8Array(this.result);
          var wavData = parseWav(wavBuffer);

          encoderWorker.addEventListener('message', function(e) {
            if (e.data.cmd == 'data') {
              cb(encode64(e.data.buffer));
            }
          });

          encoderWorker.postMessage({ cmd: 'init', config: { mode: 3, channels: 1, samplerate: wavData.sampleRate, bitrate: wavData.bitsPerSample } });
          encoderWorker.postMessage({ cmd: 'encode', buf: Uint8ArrayToFloat32Array(wavData.samples) });
          encoderWorker.postMessage({ cmd: 'finish' });
        });

        fileReader.readAsArrayBuffer(blob);
      });
    }




    // event listener for return values of the recorderWorker
    recorderWorker.addEventListener('message', function(e) {
      switch(e.data.from) {
        case 'exportBlob':
          exportCallback(e.data.blob);
          break;
      };
    });


    // HELPER FUNCTIONS

    function encode64(buffer) {
        var binary  = '';
      var bytes   = new Uint8Array(buffer);
        var len     = bytes.byteLength;

        for(var i = 0; i < len; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        return window.btoa(binary);
    }

    function parseWav(wav) {
        function readInt(i, bytes) {
            var ret = 0, shft = 0;

            while(bytes) {
                ret += wav[i] << shft; shft += 8;
                i++; bytes--;
            }
            return ret;
        }
        if(readInt(20, 2) != 1) throw 'Invalid compression code, not PCM';
        if(readInt(22, 2) != 1) throw 'Invalid number of channels, not 1';

        return { sampleRate: readInt(24, 4), bitsPerSample: readInt(34, 2), samples: wav.subarray(44) };
    }

    function Uint8ArrayToFloat32Array(u8a){
        var f32Buffer = new Float32Array(u8a.length);
        for (var i = 0; i < u8a.length; i++) {
            var value = u8a[i<<1] + (u8a[(i<<1)+1]<<8);
            if (value >= 0x8000) value |= ~0x7FFF;
            f32Buffer[i] = value / 0x8000;
        }
        return f32Buffer;
    }


    this.source.connect(this.node);
    this.node.connect(context.destination);    // this should not be necessary
  }

  window.MP3Recorder = MP3Recorder;

})(window);
0
user2164096