Web AudioでYoutube、itunes上の曲を元に、WebGL（GLSLシェーダー）でリアルタイムビジュアライズ

[WebGL Advent Calendar](http://qiita.com/advent-calendar/2015/webgl) 3日目の記事です。初めて赤くなりました！

-------------------------------------------

ブラウザで、Youtube上の曲を元にしてGLSL(WebGL)でリアルタイムビジュアライズします。応用編としてitunesの曲や自分の声でもできるようになりました。（無理矢理）

Web Audio APIのアナライザーノードというのは使ったことがなかったんですが、[mdnのデモ](https://mdn.github.io/voice-change-o-matic/)を見ていて、結構感度が良かったのでゴニョゴニョやってみる。

↓イメージ（canvasとvideoの二枚重ね）
![スクリーンショット-2015-12-02-20.33.01.png](https://qiita-image-store.s3.amazonaws.com/0/57193/37280609-712c-2ed7-c07a-3e0c416e1bf3.png)

音のソースとしてYoutube（実際にはマスターのオーディオ）を使い、背景でも流しつつ、Web Audio APIで取得します。
その上にcanvasを置いて、WebGL経由でGLSLのシェーダーを置いていきます。
最後に音域データを料理して、シェーダーに反応させます。ビートを刻んでいる感じになりました。



##何はともあれ、demoをご覧ください
音量によって変わるので、各自のマスターの音量を調節してください。かなり大きめで開発したので大きめがおすすめ。ちなみにミュートにして声を出すと声に反応します。

demo  : https://hp0me.github.io/glsl_webaudio_visualizer/
(要Chrome or FF &マイク許可。光が良い感じになるようにボリュームを調整してください。)

code : https://github.com/hp0me/glsl_webaudio_visualizer

![29g51155224222822233323233uurll3k3.gif](https://qiita-image-store.s3.amazonaws.com/0/57193/6a8d4f24-2a61-5c67-19fd-fd726c8f551a.gif)




##Web Audio APIのアナライザーノードで、曲を音域ごとに区切っていく
音はWeb Audio API(HTML5)のcreateAnalyser()で分析します。

そのデータを、
・音域の総和を色に変換する。
・音域を区切る。
・リズムを刻むようにうまいことオフセットをかける。

・WebAudioのアナライザー詳細
http://www.g200kg.com/jp/docs/webaudio/analyser.html

・曲はDave Crowe氏のビートボックス。この動画単体でもすごい
https://www.youtube.com/watch?v=Onr12llWDGo

https://mdn.github.io/voice-change-o-matic/
これをみていけるなと思ったんです。

このAPIに関して、MDNに詳細あり
https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteTimeDomainData

```JS:WebAudioで音を区切る部分
var audioCtx = new AudioContext();
var analyser = audioCtx.createAnalyser();
analyser.minDecibels = -90; //最小値
analyser.maxDecibels = 0; //最大値
analyser.smoothingTimeConstant = 0.65; //落ち着くまでの時間

analyser.fftSize = 32; //音域の数
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);

var bufferLength = analyser.frequencyBinCount;

//↓の配列に音域ごとの大きさが入る
var dataArray = new Uint8Array(bufferLength);

var source;

//Youtubeの音を拾う
navigator.webkitGetUserMedia (
  {
     audio: true
  },
  function(stream) {
    source = audioCtx.createMediaStreamSource(stream);
    source.connect(analyser);
		getAudio();
  },
  function(err) {
     console.log(err);
  }
);

//dataArrayに音域情報を入れる。繰り返す
function getAudio() {
  requestAnimationFrame(getAudio);
  analyser.getByteFrequencyData(dataArray);
}
```




##youtubeをバックグラウンド再生してcanvasを重ねる

動画の背景埋め込みには↓の方法を採用
「baserJSで埋め込んだYouTubeを背景動画にする」
http://qiita.com/YusukeHirao/items/b99b591fa9765763d508

```js
$('.youtube').bcYoutube().bcBackground();
```

その上に重ねたcanvasに、opacityをかけることで背景の動画と混ざり合うわけです。
音はYoutubeから鳴っている音がWebAudioのInになります。

##GLSLでシェーダーを書く

GLSLシェーダーの書き方は@doxasさんの記事にたくさんあるので、最早まとめるまでもない雰囲気

「こぴぺするだけ簡単 WebGL + GLSL テンプレート」
http://qiita.com/doxas/items/000f2ad6f82c27bd389e


さきほどの区切った音域ごとの値をもらってきます。

```html
<script id="fs" type="xs/fs">
precision mediump float;
uniform float rad; // 回転のステップ
uniform float circleSize; // ◯の大きさ
uniform float blueIntensity; // 青の強さ
uniform float redIntensity; // 赤の強さ
uniform vec2  resolution;

void main(void)
{

  vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / max(resolution.x, resolution.y);

  vec3 color = vec3(0.0);

  //回る◯パーティクル、たすきがけ
  for(float i = 0.0; i < 6.0; i++){
      vec2 q = p + vec2(cos(rad + i) + circleSize * 30.0, sin(rad + i + 2.0) + circleSize * 30.0 - 0.3) * 0.5;
      color.g += circleSize / length(q * 1.5);
      color.b += circleSize / length(q * 1.5) * 0.6;
  }
  for(float i = 0.0; i < 6.0; i++){
      vec2 q = p + vec2(cos(rad - i), sin(rad + i + 2.0) + circleSize * 30.0 - 0.3) * 0.5;
      color.g += circleSize / length(q * 1.5) * 0.6;
      color.b += circleSize / length(q * 1.5) * 0.6;
  }

  //青、赤の強さを決定
  color.b += blueIntensity;
  color.r += redIntensity;

  //色を決定
  gl_FragColor = vec4(color, 1.0);

}

</script>

<script id="vs" type="xs/vs">
//今回はいじらない
attribute vec3 position;
void main(void)
{
  gl_Position = vec4(position, 1.0);
}
</script>
```

##シェーダーにオーディオの分析データを料理して送る
いい感じの値になるようにさばきます。ここは最大のポイント。アルゴリズムやパラメータのチューニング次第で面白くなったりつまらなくなったりします。

・青は全ての音域の総和を拾うようにします。こうすることで曲の盛り上がり、盛り下がりを拾う効果を狙う。

・赤は特定音域だけを広います。これで曲のブレーク中、ある音域だけが強調されて青とうまく混ざる。

・◯の軌道、これはある値より低い時は戻るようにします。こうすると、ビートが強調される。プラス、ブレイクの時とかにシューンと戻っていくのでブレイク感が出ます。それと大きさは青に比例するようにします。

```js:音をさばく部分
//音域の総和を得る
var dNum = 0;
for(var i = 0; i < dataArray.length;i++){
  dataArray[i] *= 2.5; //増幅させる
  dNum += dataArray[i];
}

//青は、総和をビジュアライズする
var blue = dNum * 0.00023;

//赤は特定の音域のみ拾う
var red = dataArray[6] * 0.002;

//◯の軌道。低い時は戻るようにする
var vr = dataArray[6] * 0.0003;

if(vr <= 0.04){
  vr -= 0.02;
}else{
  vr += 0.04;
}

//◯の大きさは青に比例する
var circleSize = red * 0.01 +  blue * 0.04;
rad += vr;
```

##思わぬ副産物・・・こっちがメインか？！
音量をMacのミュートボタンで切って、マイクに向かってボイスパーカッションとかをすると面白いです。というかこういう用途の方がありかも。というかitunesのビジュアライザーになるやん。

ということでこっちの方も作ってみました。:tada:
demo : https://hp0me.github.io/glsl_webaudio_visualizer/you.html

とはいっても、このままだとマイクの音も入って、曲の大事な部分で邪魔をしたりしてしまいます。こういう時は、環境設定でマイクのインプットを切ります。ｗ
![スクリーンショット 2015-12-02 21.24.04.png](https://qiita-image-store.s3.amazonaws.com/0/57193/bc4af988-9cb0-b5c6-934c-b46d881dd1bd.png)

若干無理矢理ですが、itunesのビジュアライザーの完成！（他の音を出さなければね・・・）