JavaScriptだけ!ブラウザで録音&ファイル保存する方法

さてさて、前々回、意外と簡単!音を感知して録画する監視カメラをつくるでは、Pythonで大きな音を感知して録画するという機能をつくってみました。

ちなみになぜPythonで作ったかと言うと、記事の中でも書いたとおりJavaScriptで音量をチェックする方法が見つからなかったので仕方なく、、といった部分があったからでした。(もしご存知の方がいらっしゃったら、ぜひ教えてくださいm_ _m)

そして、この件の調査で知ることになったのですが、なんと、

ブラウザだけで録音をし、さらにそれをファイルとしてダウンロードすることができる

ようなので、今回はこの機能をご紹介したいと思います。

ぜひ皆さんのお役に立てると嬉しいです😊✨

開発環境: Vue 2.6

前提として

今回の機能を実装するためには、マイクにアクセスする必要があります。そして、Google Chromeからマイクにアクセスするためには、たとえローカル環境であってもHTTPS接続が必須となっています。

そのため、まだHTTPS接続を準備していない方は先にそちらを用意してから実行してください。なお、nginxの場合は以下の記事を参考にしてみてください。

コピペでOK!ローカル環境にHTTPSを導入する(nginx編)

コードをつくっていく

今回は特にコードが短いので、基本コードの番号を各コードと入れ替えると完成する「学習雑誌の付録」形式でお届けします。

基本コード

ではまずは基本コードです。

このコードの中に書かれている①〜⑤の場所にこれから説明していくコードをセットしていってください。

<html>
<head>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="p-3">
    <h1 class="mb-3">JavaScriptで録音するサンプル</h1>
    <div id="app">
        <!-- ③ 録音の開始/終了ボタンを設置する部分 -->
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
    <script>

        new Vue({
            el: '#app',
            data: {

                // ① 変数を宣言する部分

            },
            methods: {

                // ② 録音を開始/ストップする部分

                // ④ 音声ファイルの拡張子を取得する部分

            },
            mounted() {

                // ⑤ マイクにアクセス

            }
        });

    </script>
</body>
</html>

では実際にやっていきましょう!

変数を宣言する部分 ・・・ ①

これからいくつかメソッドなどを作っていきますが、その際に必要となってくる変数を以下のように定義しておきましょう。

// ① 変数を宣言する部分
status: 'init',     // 状況
recorder: null,     // 音声にアクセスする "MediaRecorder" のインスタンス
audioData: [],      // 入力された音声データ
audioExtension: ''  // 音声ファイルの拡張子

まずstatusは、そのページの状況がわかるよう、以下3つの文字列が保持されることになります。

  • init ・・・ ページが読み込まれたばかり
  • ready ・・・ 録音が開始できる状態
  • recording ・・・ 録音中

次にrecorderaudioDataですが、手順としてはrecorderを通して音声データを取得してaudioDataに格納、そしてそのデータを加工してファイル保存するという流れになります。

最後にaudioExtensionですが、これは音声ファイルの拡張を自動抽出し保持しておくための変数です。というのも、Google Chromeの音声ファイル形式はwebmですが、firefoxの場合はoggとなっているためです。

録音を開始/ストップする部分 ・・・ ②

続いて実際に録音を開始&ストップする部分(メソッド)です。

startRecording() {

    this.status = 'recording';
    this.audioData = [];
    this.recorder.start();

},
stopRecording() {

    this.recorder.stop();
    this.status = 'ready';

},

recorderMediaRecorderのインスタンスですが、これは後ほどmounted()の中で定義することになります。

そして、recorderはシンプルにstart()stop()メソッドを実行することで録音を開始/終了することができます。

なお、startRecording()ではaudioDataを初期化していて、さらにそれぞれのメソッド内でstatusを切り替えていることに注目してください。このstatusの値は、次の項目で使うことになります。

録音の開始/終了ボタンを設置する部分 ・・・ ③

次に実際にブラウザ表示される録音の開始&終了ボタンです。

<button class="btn btn-danger" type="button" v-if="status=='ready'" @click="startRecording">録音を開始する</button>
<button class="btn btn-primary" type="button" v-if="status=='recording'" @click="stopRecording">録音を終了する</button>

ここで重要なのが、v-ifの部分です。これは変数statusが変化したときに以下のようなにボタンを表示/非表示するためです。

  • ready ・・・ 「録音を開始する」ボタンを表示
  • recording ・・・ 「録音を終了する」ボタンを表示

そして、それぞれのクリックイベントで先ほどつくったstartRecording()stopRecording()を実行しています。

音声ファイルの拡張子を取得する部分 ・・・ ④

続いて①の作業でつくった変数audioExtensionに格納することになる「音声ファイルの拡張子」を取得するメソッドをつくっておきましょう。(このメソッドは次の項目で使います)

getExtension(audioType) {

    let extension = 'wav';
    const matches = audioType.match(/audio\/([^;]+)/);

    if(matches) {

        extension = matches[1];

    }

    return '.'+ extension;

}

音声ファイルの形式は、recorderから取得できるのですが、中身は以下のようなものになっているため、正規表現で必要な部分を切り出して拡張子としています。

audio/webm;codecs=opus

マイクにアクセス ・・・ ⑤

では、最後に今回で一番重要な部分です。

navigator.mediaDevices.getUserMedia({ audio: true })
    .then(stream => {

        this.recorder = new MediaRecorder(stream);
        this.recorder.addEventListener('dataavailable', e => {

            this.audioData.push(e.data);
            this.audioExtension = this.getExtension(e.data.type);

        });
        this.recorder.addEventListener('stop', () => {

            const audioBlob = new Blob(this.audioData);
            const url = URL.createObjectURL(audioBlob);
            let a = document.createElement('a');
            a.href = url;
            a.download = Math.floor(Date.now() / 1000) + this.audioExtension;
            document.body.appendChild(a);
            a.click();

        });
        this.status = 'ready';

    });

この中ではまず最初に以下の部分でマイクにアクセスする処理を実行します。

navigator.mediaDevices.getUserMedia({ audio: true })

※ ここで、もしブラウザにマイクへのアクセスがまだ許可されていない場合は以下のようなポップアップが表示されることになります。

そして、許可されるとthen()の中身が実行されます。

この中では、取得されたstreamを使ってMediaRecorderのインスタンスを作り、変数recorderとして保持します。

さらに、そのrecorderには2つのイベントをつけます。

1つめが、dataavailableで、ここでは音声データを取得し、さらにそれを変数audioDataに格納しています。また、音声ファイルの拡張子も同じタイミングで取得することになります。

そして、もう一つのイベントがstopで文字通り録音が終了したときに実行されるイベントになっています。

この中では音声データをBlob化し、<a download="***">タグのhrefとして使用されます。そしてこの<a>タグはページに追加されることになり、click()メソッドを実行することで「クリックしたこと」になり、ファイルのダウンロードを実現しています。

テストしてみる

短いですが、実際に「エリーゼのために」の冒頭部分をスマホのアプリで演奏したものが以下になります。
webm形式ですのでブラウザによっては再生できないのでご注意ください。

ダウンロードする

今回開発したソースコードを以下からダウンロードすることができます。なお、base.htmlは基本コードで、complete.htmlが完成版のコードになります。

ブラウザで録音&ファイル保存する

おわりに

ということで、今回はブラウザだけで録音&ファイル保存する方法を紹介しました。

最近はブラウザとはいえども、ネイティブアプリのようなことができるようになってきているので、我々ウェブ開発をする人間からするとワクワクすることが多くなっています(笑)

この機能を応用すると、動画に声を吹き込む機能などもつくれるようになると思うので、ぜひみなさんのサイトでも活用してみてくださいね。

ではでは〜!

「気がつけばもう年末か・・・ホント早い😂」

この記事が役立ちましたらシェアお願いします😊✨