新規投稿
フォローする

MediaRecorder APIを使って録音アプリをつくろう

MediaRecorder APIは、動画や音声のようなメディアストリームを記録するためのAPIです。 音声データ処理などの知識がなくても簡単に利用することができます。

サンプル

ブラウザで録音し、kintoneに保存します。

フォーム設定

コード

HTML(カスタマイズビュー : 録音)

<link rel="stylesheet" href="//your-domain/path/sample.css">
<div id="new">
  <p>新規</p>
  <div id="recorder" class="default">
    <a id="startButton">開始</a>
    <a id="pauseButton">一時停止</a>
    <a id="resumeButton">再開</a>
    <a id="stopButton">停止</a>
    <audio id="player" controls></audio>
    <a id="deleteButton">削除</a>
    <a id="saveButton">保存</a>
  </div>
  <div id="unableRecord">
    <p>録音できません。マイクが無効になっているか、ブラウザが録音機能に対応していません。</p>
  </div>
</div>
<div id="saved">
  <p>保存済み</p>
  <table>
    <thead>
      <tr>
        <th>作成日時</th>
        <th>録音</th>
      </tr>
    </thead>
    <tbody></tbody>
  </table>
</div>
<script type="text/html" id="saved-tbody-template">
  <tr>
    <td><%= created %></td>
    <td><audio src="<%= file %>" controls></td>
  </tr>
</script>

※モバイル版に対応するため、カスタマイズビューのHTMLからCSSを読み込んでいます。 パソコン表示のみ行う場合は、通常通り「JavaScript / CSSでカスタマイズ > PC用のCSSファイル」からの読み込みで問題ありません。

JavaScript

本サンプルでは、lodash及びMoment.jsを利用しています。Cybozu CDNからご利用ください。 先にlodash.min.jsとmoment.min.jsを読み込んだ後、下記sample.jsを読み込みます。

・sample.js

(function(){
  "use strict";
  kintone.events.on([
    'app.record.index.show',
    'mobile.app.record.index.show',
  ], function(event){
    if(event.viewName !== '録音') return;
    kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
      app: (event.type === 'app.record.index.show') ? kintone.app.getId() : kintone.mobile.app.getId()
    }).then(function(response){
      kintone.Promise.all(response.records.map(function(record){
        return new kintone.Promise(function(resolve){
          var xhr = new XMLHttpRequest();
          xhr.open('GET', '/k/v1/file.json?fileKey=' + record.音声.value[0].fileKey);
          xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
          xhr.responseType = 'blob';
          xhr.addEventListener('load', function(){
            resolve({
              created: moment(record.作成日時.value).format('YYYY-MM-DD HH:mm'),
              file: (window.URL || window.webkitURL).createObjectURL(xhr.response)
            });
          });
          xhr.send();
        });
      })).then(function(rows){
        document.querySelector('#saved tbody').innerHTML = rows.reduce(function(html, row){
          return html + _.template(document.getElementById("saved-tbody-template").innerHTML)({
            created: row.created,
            file: row.file
          });
        }, '');
      });
    });
    if(!navigator.mediaDevices){
      document.getElementById('new').className = 'unable';
      return;
    }
    navigator.mediaDevices.getUserMedia({
      audio: true,
      video: false
    }).then(function(stream){
      var recorder = document.getElementById('recorder');
      var startButton = document.getElementById('startButton');
      var pauseButton = document.getElementById('pauseButton');
      var resumeButton = document.getElementById('resumeButton');
      var stopButton = document.getElementById('stopButton');
      var player = document.getElementById('player');
      var deleteButton = document.getElementById('deleteButton');
      var saveButton = document.getElementById('saveButton');
      var loadRecorder = function(){
        var mediaRecorder = new MediaRecorder(stream);
        var blob;
        mediaRecorder.addEventListener('dataavailable', function(e){
          blob = e.data;
          player.src = window.URL.createObjectURL(blob);
        });
        startButton.addEventListener('click', function(){
          mediaRecorder.start();
          recorder.className = 'started';
        });
        pauseButton.addEventListener('click', function(){
          mediaRecorder.pause();
          recorder.className = 'paused';
        });
        resumeButton.addEventListener('click', function(){
          mediaRecorder.resume();
          recorder.className = 'started';
        });
        stopButton.addEventListener('click', function(){
          mediaRecorder.stop();
          recorder.className = 'stopped';
        });
        var saveButtonListener = function(){
          var formData = new FormData();
          var xhr = new XMLHttpRequest();
          formData.append('__REQUEST_TOKEN__', kintone.getRequestToken());
          formData.append('file', blob, moment().format('YYYY-MM-DD-HH-mm-ss')+'.'+([
            {extension: 'aac', mimetype: 'audio/aac'},
            {extension: 'mid', mimetype: 'audio/midi'},
            {extension: 'mid', mimetype: 'audio/x-midi'},
            {extension: 'mp3', mimetype: 'audio/mpeg'},
            {extension: 'oga', mimetype: 'audio/ogg'},
            {extension: 'wav', mimetype: 'audio/wav'},
            {extension: 'weba', mimetype: 'audio/webm'},
            {extension: '3gp', mimetype: 'audio/3gpp'},
            {extension: '3g2', mimetype: 'audio/3gpp2'}
          ].find(function(format){
            return blob.type.indexOf(format.mimetype) !== -1;
          }) || {extension: blob.type.split(/\/|;/g)[1]}).extension);
          xhr.open('POST', encodeURI('/k/v1/file.json'));
          xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
          xhr.addEventListener('load', function(){
            kintone.api('/k/v1/record', 'POST', {
              app: (event.type === 'app.record.index.show') ? kintone.app.getId() : kintone.mobile.app.getId(),
              record: {
                音声: {
                  value: [
                    {fileKey: JSON.parse(xhr.responseText).fileKey}
                  ]
                }
              }
            }).then(function(){
              alert('保存しました。');
              location.reload();
            });
          });
          xhr.send(formData);
        };
        saveButton.addEventListener('click', saveButtonListener);
        deleteButton.addEventListener('click', function(){
          recorder.className = 'default';
          saveButton.removeEventListener('click', saveButtonListener);
          loadRecorder();
        });
      };
      loadRecorder();
      document.getElementById('new').className = 'able';
    }).catch(function(e){
      document.getElementById('new').className = 'unable';
    });
  });
})();

CSS

下記sample.cssを読み込みます。 ※モバイル版に対応する場合は、前述の通りカスタマイズビューのHTMLからCSSを読み込みます。

・sample.css

#new > div{
  display: none;
}
#new.able #recorder,
#new.unable #unableRecord{
  display: block;
}
#recorder > *{
  display: none;
}
#recorder.default #startButton,
#recorder.started #pauseButton,
#recorder.started #stopButton,
#recorder.paused #resumeButton,
#recorder.paused #stopButton,
#recorder.stopped #deleteButton,
#recorder.stopped #saveButton,
#recorder.stopped #player{
  display: inline;
}
#new,
#saved{
  padding: 5px 20px;
}
audio{
  max-width: 100%;
}
#saved th,
#saved td{
  border: 1px solid #000;
  padding: 5px;
}

動作環境

現状、MediaRecorder APIを利用できるブラウザは限られています。

MDN

Can I use

・筆者確認

iOSでは利用できないようですね。

1

0件のコメント

サインインしてコメントを残してください。