LoginSignup
11
4

More than 5 years have passed since last update.

ブラウザでgetUserMediaしたストリームからflacファイルを作成してみる

Last updated at Posted at 2017-12-26

これは2017年WebRTCアドベントカレンダーの23日目の記事です。

はじめに

最近、話題のスマートスピーカーGoogle HomeのCMようなことブラウザでもやりたい。

ということで

いろいろ調べてみると最もお手軽なのはWeb_Speech_APIしたが、ブラウザ互換性を見ると
Safari未サポートということなのでcloud speech API用の音声ファイルを作ってみます。

ちなみにGoogle Cloud Speech APILINEAR16 または MULAW でエンコードされたwavファイルも対応
しているのでブラウザでマイク音声からwavファイルを生成する手引 1
こちらで十分なのですが

今回は違う形式の音声ファイル(flac)をブラウザで作成したいので
ライブラリないかなと探していた所、いいのありました!

ライブラリ

Flacエンコード用
https://github.com/mmig/libflac.js
MIT License

Blob保存
https://github.com/eligrey/FileSaver.js
MIT License

WebWorkerを使ったDemoも用意されてます
https://mmig.github.io/speech-to-flac/

実装方法

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>flac recorder</title>
</head>
<body>
    <input type="button" id="start-button" onclick="startRecording();" value="Rec"></input>
    <input type="button" id="stop-button" onclick="stopRecording();" value="Stop"></input>
    <script type="text/javascript" src="libflac3-1.3.2.min.js" /></script>
    <script type="text/javascript" src="FileSaver.min.js" /></script>
    <script type="text/javascript">
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia;
    window.URL = window.URL || window.webkitURL;

    var encoder = null;
    var recording = false;
    var localStream = null;
    var audio_context = null;
    var source = null;
    var node = null;

    function startRecording() {
        //worker開始
        encoder = new Worker('encoder.js');
        encoder.onmessage = function(evt) {
            if (evt.data.cmd == 'end') {
                //flac保存処理
                saveAs(evt.data.buf, 'output.flac');
                //worker破棄
                encoder.terminate();
                encoder = null;
            }
        };

        navigator.getUserMedia({ video: false, audio: true },
            function(stream) { // for success case
                localStream = stream;
                gotStream(localStream);
            },
            function(err) { // for error case
                console.log(err);
            }
        );
    }


    function gotStream(stream) {
        console.log('start recording');
        recording = true;

        var audio_context;
        if (typeof webkitAudioContext !== 'undefined') {
            audio_context = new webkitAudioContext;
        } else if (typeof AudioContext !== 'undefined') {
            audio_context = new AudioContext;
        }
        source = audio_context.createMediaStreamSource(stream);

        if (source.context.createJavaScriptNode) {
            node = source.context.createJavaScriptNode(4096, 1, 1);

        } else if (source.context.createScriptProcessor) {
            node = source.context.createScriptProcessor(4096, 1, 1);
        }

        var samplerate = audio_context.sampleRate; //44100 または 48000
        //flac初期処理
        encoder.postMessage({ cmd: 'init', config: { samplerate: samplerate, bps: 16, channels: 1, compression: 5 } });

        node.onaudioprocess = function(evt) {
            if (!recording) {
                return;
            }

            var channel = evt.inputBuffer.getChannelData(0);
           //flacエンコード
            encoder.postMessage({ cmd: 'encode', buf: channel });
        };

        source.connect(node);
        node.connect(audio_context.destination);
    }

    function stopRecording() {
        if (!recording) {
            return;
        }
        console.log('stop recording');
        var tracks = localStream.getAudioTracks()
        for (var i = tracks.length - 1; i >= 0; --i) {
            tracks[i].stop();
        }
        recording = false;
        //worker終了
        encoder.postMessage({ cmd: 'finish' });

        source.disconnect();
        node.disconnect();
        source = node = null;
    };
    </script>
</body>

</html>

おわりに

そのうちMediaRecorderのmimeTypeにflacが追加されるのを期待しますw!

11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4