HTML5でマイクで拾った音を色々して何か作った。

なんか面白いものを作ろうかと思って、HTML5のAnalyserNodeとやらを使ってみました。 細かいことはともかくとして、大雑把に使い方を。

なんかこんな感じのやつが出来る。マイクの付いた新しめのブラウザでご覧下さい。

ソースコードはこんな感じです。

<audio muted></audio>
<canvas width=640 height=480></canvas>
<script>
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');

canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;

navigator.getUserMedia(
    {audio : true},
    function(stream){
        document.querySelector('audio').src = URL.createObjectURL(stream);
        var audioContext = new AudioContext();
        var analyser = audioContext.createAnalyser();
        var timeDomain = new Float32Array(analyser.frequencyBinCount);
        var frequency = new Uint8Array(analyser.frequencyBinCount);
        audioContext.createMediaStreamSource(stream).connect(analyser);

        (function animation(){
            analyser.getFloatTimeDomainData(timeDomain);
            analyser.getByteFrequencyData(frequency);

            context.clearRect(0, 0, canvas.width, canvas.height);

            context.strokeStyle = 'blue';
            context.beginPath();
            context.moveTo(0, canvas.height - frequency[0]*canvas.height/255);
            for(var i=0; i<frequency.length; i++){
                context.lineTo(
                    i*canvas.width/frequency.length,
                    canvas.height - Math.max(0, frequency[i]*canvas.height/255)
                );
            }
            context.stroke();

            context.strokeStyle = 'red';
            context.beginPath();
            context.moveTo(0, canvas.height/2 + timeDomain[0]*canvas.height/2);
            for(var i=0; i<timeDomain.length; i++){
                context.lineTo(
                    i*canvas.width/timeDomain.length,
                    canvas.height/2 + timeDomain[i]*canvas.height/2
                );
            }
            context.stroke();

            requestAnimationFrame(animation);
        })();

    },
    console.log
);
</script>

配列とAudioContextとやらを作って、AnalyzerNodeAudioNodeに接続。 あとはgetFloatTimeDomainDataってやつで波形、getByteFrequencyDataってやつで周波数(波形をフーリエ変換したやつ)を取得出来ます。 どちらもgetFloatを使うと浮動小数点で、getByteから始まるやつを使うと0から255までの値で取得出来ます。分かりやすくて良いね。

マイクからリアルタイムに音を取得したい場合はcreateMediaStreamSourceの代わりにcreateMediaElementSourceを使います。大体こんな感じ。

<audio src="music.mp3" controls></audio>
<script>
    var audioContext = new AudioContext();
    var analyser = audioContext.createAnalyser();
    audioContext.createMediaElementSource(document.querySelector('audio')).connect(analyser);
</script>

それ以外の使い方は一緒です。getUserMediaとかやらなくて良いだけもっとシンプル。

普通にcanvasを使っているので、わりと何でも出来ます。楽しい。

余談ですが、google chromeでgetUserMediaを使おうとする場合はhttpsを使わないといけないようです。Firefoxとかは分からん。 そんなわけでこのページもHTTPSになってるはず。