【Vue3】ブラウザでマイク音声を録音する(Composition API)

# やりたいこと

ブラウザで録音するアプリケーションを作ります。

Vue2.xやOptions API で書かれたサンプルコードは色々見つけましたが、Vue3 のComposition API で書かれたコードは見つからなかったので、参考に共有します。

# 基本

ブラウザでの録音はMediaStream Recording APIを使います。

基本的には以下のソースコードと解説を参考にしました。

[MediaStream Recording API の使用 - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API)

# 全コード

短いので、テンプレートの全コードを掲載します。

```jsx
<template>
    <button @click="startRecording()" v-if="!record.state">
            録音
    </button>
    <button @click="stopRecording()" v-if="record.state">
            停止
    </button>

    <article class="clip">
    <audio ref="audioPlayer" controls></audio>
    </article>
</template>

<script>
    import { ref, reactive, defineComponent } from 'vue'

    export default defineComponent ({
        setup () {
            const record = reactive({ //録音中か停止中か判定：録音ボタン・停止ボタン切り替え
                state: false,
            });
            const audioPlayer = ref(); //<audio>タグを取得
            let recorder = null; //MediaRecorderに使う変数を宣言
            let mimeType = ''; //音声ファイルのタイプを取得
            let chunks = []; //録音のblobを挿入する配列

						//マイクが使用可能か判定
            if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
            console.log('getUserMedia supported.');
            navigator.mediaDevices.getUserMedia (
                // constraints - only audio needed for this app
                {
                    audio: true
                })

                // Success callback
                .then(function(stream) {
                    recorder = new MediaRecorder(stream);          
                    recorder.ondataavailable = function(e) {
                        mimeType = e.data.type;
                        chunks.push(e.data);
                    }

										//ここはデバッグ用に追記。無くてもよい
                    recorder.onstart = function() {
                        console.log('started');
                    }

                    recorder.onstop = function() {
                        const blob = new Blob(chunks, { 'type' : mimeType });
                        chunks = [];
                        const audioURL = window.URL.createObjectURL(blob);
                        audioPlayer.value.src = audioURL;
                    }
                })

                // Error callback
                .catch(function(err) {
                    console.log('The following getUserMedia error occured: ' + err);
                }
            );
            } else {
            console.log('getUserMedia not supported on your browser!');
            }

                
            const startRecording = () => {
                record.state = true;
                recorder.start();
            }

            const stopRecording = () => {
                record.state = false;
                recorder.stop();
            }

            return {
                record, startRecording, stopRecording, audioPlayer
            }
        }

    })
</script>
```

# ポイント

## setup に記述

これはComposition APIの記法ですが、setup は以前のcreated とほぼ同じです。

そしてsetup 内にdata や methods に相当する内容も全て定義します。

マイクの利用可否を判定する箇所も、Options API ではcreatedかmounted に記載していた内容です。これを setup 内に直接書いています。

```jsx
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
//....
}
```

## 変数宣言

冒頭でいくつか変数を宣言しています。

```jsx
const record = reactive({ //録音中か停止中か判定：録音ボタン・停止ボタン切り替え
    state: false,
});
const audioPlayer = ref(); //<audio>タグを取得
let recorder = null; //MediaRecorderに使う変数を宣言
let mimeType = ''; //音声ファイルのタイプを取得
let chunks = []; //録音のblobを挿入する配列
```

record はボタンの表示切り替え用。他のkey も増やすことを考えてreactive で宣言しています。

audioPlayer で、audio のrefを取得しています。

重要なのは recorder と chunks。ここで宣言していないと他のfunctionで利用できません。

また、const では上手く動作せず、let で宣言することによって動作しました。

再代入をするから、という理由だと思いますが、まだ理解できていません。できればconst で宣言するように書き換えたいのですが。。

[【JavaScript】var / let / const を本気で使い分けてみた - Qiita](https://qiita.com/cheez921/items/7b57835cb76e70dd0fc4)

# 最後に

Composition API の記法への慣れが必要ですが、ポイントを押さえれば少ないコードで実装できます。

エンコーディングやマイクのゲイン調整などさらに複雑なことをしたい場合は、プラグインを活用するなどした方がいいかなと思います。

[Next.js + RecorderService.js でブラウザで音声を録音する 🎙 - みかづきブログ・カスタム](https://blog.kimizuka.org/entry/2021/01/28/104601)

[https://github.com/kaliatech/web-audio-recording-tests](https://github.com/kaliatech/web-audio-recording-tests)

# その他の参考URL
[JavaScriptだけ！ブラウザで録音＆ファイル保存する方法](https://blog.capilano-fw.com/?p=5351)

[getUserMedia、MediaRecorderを使ってブラウザで録音する | バシャログ。](https://bashalog.c-brains.jp/21/09/10-102902.php)