Web上で録音する機能を最低限のコードで分かりやすく解説【WebRecorder】

## 経緯と目的
WebRecorderAPIを使えば無料で短いコードでWeb上に録音機能を実装できます。
しかし専門的な分野なので分かりやすい資料は少ないです。
今回は音声を扱ったことが無い方に向けてなるべく専門用語を使わずに解説していきます。
これから記述していくAPIメソッドや音声の処理について詳しく知りたい方は各公式ドキュメント等を参照しながら理解を深めていただけたら幸いです。

## 実装する機能とイメージ
1. 録音、保存
1. 再生、ダウンロード
1. コンソールには録音のステータスを表示
![audiosample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233009/0492b25e-6d92-df30-e2a9-112d85a11495.png)

## HTML
```html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SampleAudioSystem</title>
</head>
<body>
    <button id="start">Start</button>
    <button id="stop">Stop</button>
    <p></p>
    <audio id="audio" controls></audio>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>
```
`<audio>` に、属性 `controls`を与えるだけで ↓ が出現します。
こちら録音後にデフォルトでダウンロードまで可能です。
![controls.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233009/802fb73d-3504-8823-20ff-35198cae5fc9.png)
## JavaScript
###処理の概要
録音とは一言ですが、少なくとも今回の処理では三段階に分割して考えます。

1. 音声入力の受付
1. 音声データの保存
1. 保存したデータをブラウザに渡して操作

## コード全文
簡略化のためエラー回避やブラウザ互換などは記述していません。PC版Chromeで動作確認済み。

```JavaScript:
var startButton = document.getElementById("start");
var stopButton = document.getElementById("stop");
var mediaRecorder;
var localStream;

startButton.onclick = function () {
    navigator.mediaDevices.getUserMedia({audio: true })
        .then(function (stream) {
            localStream = stream;
            mediaRecorder = new MediaRecorder(stream);
            mediaRecorder.start();
            console.log("Status: " + mediaRecorder.state);
        }).catch(function (err) {
            console.log(err);
        });
}
stopButton.onclick = function () {
    mediaRecorder.stop();
    console.log("Status: " + mediaRecorder.state);
    mediaRecorder.ondataavailable = function (event) {
        document.getElementById("audio").src = window.URL.createObjectURL(event.data);
    }
    localStream.getTracks().forEach(track => track.stop());
}
```
## 部分別解説

### 1.音声入力の受付　navigator.mediaDevices.getUserMedia(constraints)
引数にマイク or カメラを指定してデバイスの使用許可を表示、許可すると音声受付が開始される。
`stream` とは受け取った音声データのことで、それを使って処理を記述する。

```javascript:
navigator.mediaDevices.getUserMedia({audio: true })
  .then(function (stream) {
    // 受け取った音声の処理を記述。次項ではここの処理を記述していく。
  })
  .catch(function (err) {
    // エラー時の処理
  });
```
![mic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233009/84234903-7819-ed0c-aeb2-a455534c1c58.png)
### 2.音声データの保存　MediaRecorder()
 録音するためのAPI。引数には録音する音声データである `stream` を与える。
保存先はWeb上にBLOBというデータ型で保存される。

```javascript:
mediaRecorder = new MediaRecorder(stream); // 録音する為のインスタンス作成
mediaRecorder.start(); // 保存開始
mediaRecorder.stop(); // 保存終了
mediaRecorder.state // 録音ステータスが文字列で返って来る。
mediaRecorder.dataavalable = function (event) {
    // 保存が完了すると発火。保存されたデータは `event.data` に格納され、処理を記述できる。
}
```
### 3.保存したデータをブラウザに渡して操作　window.URL.createObjectURL(event.data)
保存された音声データのURLを発行、そして\<audio>に渡すことで操作が出来るようになります。

```javascript:
var audioURL = window.URL.createObjectURL(event.data);
document.getElementsByClassName("audio").src = audioURL;
```

### 4.補足

`mediaRecorder.stop()` で保存は終了しても、音声入力の受付は止まりません。そもそも二つのAPIを使っているのでどちらも終了させる必要があります。

```javascript:
localStream.getTracks().forEach(track => track.stop());
```

以上のコードを録音開始、終了それぞれのクリック時の処理に含めることにより完成します。