d3.js
音楽
データ分析
WebAudioAPI
データ可視化

Web Audio APIとD3.jsを使ってスターウォーズのテーマソングを可視化してみた

More than 1 year has passed since last update.

この記事はフロムスクラッチ Advent Calendar 2016の25日目の記事です。


ども('ω')ノ

take_chan_manです。

今年のクリスマスは「Star Wars Rogue One」を観れたので個人的にはめちゃくちゃ満足してます。

スターウォーズシリーズの中でも上位を争う出来だと思いますし、スターウォーズを観たことない人でも楽しめる内容だと思うので、まだ観てない人は年末年始に是非映画館で観てください!!

image

さて、今日はLittleBitsを使って音楽に合わせて光るツリーを作ってみるはずだったのですが、、、

まさかのLittleBitsArduinoモジュール不足のため、音楽をWeb Audio APID3.jsを使って可視化する方法について書いてみたいと思います( ..)φカキカキ(はい、完全に言い訳です。すみませんm(__)m)


想定読者


  • 「音楽を可視化するのって面白そうじゃん!」と思うエンジニア:robot:

  • Web Audio APIに興味のあるエンジニア:robot:

  • D3.jsに興味のあるエンジニア:robot:

  • スターウォーズが好きな人:grinning:


この記事の目的


  • 音楽データを可視化する

  • Web Audio APIの使い方についてなんとなく理解する

  • D3.jsの使い方についてなんとなく理解する

  • スターウォーズのテーマソングを愉しむ


用意するもの

必要なものは3つだけです!お手軽です!!

①スターウォーズのテーマソング(music.mp3)

②HTML(index.html)

③JavaScript(app.js)

image

では、それぞれ準備してみましょう!


①スターウォーズのテーマソング(music.mp3)

僕が使ったのはベスト・オブ・スター・ウォーズ ~ミュージック・アンソロジー~ の「メイン・タイトル/ブロッケード・ランナー」という曲です。音楽自体は何でも良いので自分の好きな曲のMP3ファイルをご用意ください。

image


②HTML(index.html)

こんな感じのHTMLファイルを用意します。


index.html

<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Audio Visualization Test</title>
<style>
body {
text-align: center;
font-family: monospace;
line-height: 2.0;
background-color: #000000;
padding: 20px;
}
button {
font-size: 16px;
background: #000000;
color: #7ddc1f;
border: none;
outline:none;
padding: 4px 8px;
letter-spacing: 1px;
}
button:hover {
background: #7ddc1f;
color: #000000;
}
</style>
</head>

<body>
<audio id="audioElement" src="music.mp3"></audio>
<div>
<button onclick="document.getElementById('audioElement').play()">Play the Music</button>
<button onclick="document.getElementById('audioElement').pause()">Stop the Music</button>
</div>
<div id="graph"></div>
<script src="http://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script src="app.js"></script>
</body>
</html>


見た目はこんな感じになります。

image

一応、簡単にHTMLファイルの説明もしておきます。


head

黒背景に緑の文字で中央揃えにしてます。

デザインを変えたい人は適当に下記のstyle部分をを変更してください。


index.html

    <style>

body {
text-align: center;
font-family: monospace;
line-height: 2.0;
background-color: #000000;
padding: 20px;
}
button {
font-size: 16px;
background: #000000;
color: #7ddc1f;
border: none;
outline:none;
padding: 4px 8px;
letter-spacing: 1px;
}
button:hover {
background: #7ddc1f;
color: #000000;
}
</style>


body

1.可視化したい音楽ファイルを指定します。


index.html

    <audio id="audioElement" src="music.mp3"></audio>


2.音楽の再生ボタンと停止ボタンを用意します。


index.html

    <div>

<button onclick="document.getElementById('audioElement').play()">Play the Music</button>
<button onclick="document.getElementById('audioElement').pause()">Stop the Music</button>
</div>

3."graph"というIDの要素を定義します。

ここにD3チャートを描写します。


index.html

    <div id="graph"></div>


4.「D3.js」と「app.js」を定義します。

「D3.js」はminified版で十分です。「app.js」はこれから作成するJavaScriptファイルです。


index.html

    <script src="http://d3js.org/d3.v4.min.js" charset="utf-8"></script>

<script src="app.js"></script>


③JavaScript(app.js)

こんな感じのJSファイルを用意します。


app.js

(function(){

'use strict';

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var audioElement = document.getElementById('audioElement');
var audioSrc = audioCtx.createMediaElementSource(audioElement);
var analyser = audioCtx.createAnalyser();

audioSrc.connect(analyser);
audioSrc.connect(audioCtx.destination);

var frequencyData = new Uint8Array(100);

var svgHeight ='600';
var svgWidth = '1000';
var barPadding = '5';

function createSvg(parent, height, width) {
return d3.select(parent).append('svg').attr('height', height).attr('width', width);
}

var graph = createSvg('#graph', svgHeight, svgWidth);

graph.selectAll('rect')
.data(frequencyData)
.enter()
.append('rect')
.attr('x', function (d, i) {
return i * (svgWidth / frequencyData.length);
})
.attr('width', svgWidth / frequencyData.length - barPadding);

function renderChart() {
requestAnimationFrame(renderChart);

analyser.getByteFrequencyData(frequencyData);

graph.selectAll('rect')
.data(frequencyData)
.attr('y', function(d) {
return svgHeight - (d * 2);
})
.attr('height', function(d) {
return d * 2;
})
.attr('fill', function(d) {
return 'rgb(0, ' + d + ', 0)';
});
}

renderChart();

}());



1.Web Audio APIを使って音楽データを配列に突っ込む

まずはWeb Audio API使って音楽データを可視化する準備をします。


Web Audio APIとは?

Mozilla Developer Networkより引用:


Web Audio API は音声操作をオーディオコンテキスト内の操作として実現し、モジュラールーティングできるようにデザインされています。基本的な操作は オーディオノードとして表現されています。これを接続することで、オーディオグラフを作成します。 チャンネル構成の異なる複数の音源も 1 つのコンテキスト内で扱えます。この構成によって、複雑で動的な音声操作を実現できるようになっています。

オーディオノードは入力と出力を接続され、一つや多数のソースからいくつかのノードを辿ってdestinationに到達するチェインを形成します。(音声の可視化を行う場合など、 destination へは到達しないこともあります) 通常のWeb Audioの使い方は次のようになります:

1. オーディオコンテキストを作成する

2. コンテキストの中で、,オシレーター,ストリームなどのソースを作成する

3. リバーブ・フィルター・パンナー・コンプレッサーなどのエフェクトノードを作成する

4. 最終的な音声の到達先を選ぶ(例えばスピーカー)

5. ソースをエフェクトに繋げ、エフェクトを到達先(destination)に繋げる


image


なんだか難しそうですね。とりあえずやってみよう!


手順

1-1.Web Audio APIを利用するためのインスタンスを生成します。


app.js

    var audioCtx = new (window.AudioContext || window.webkitAudioContext)();


1-2.MediaElementSourceNodeにindex.htmlにて定義したaudioElementを指定します。


app.js

    var audioElement = document.getElementById('audioElement');

var audioSrc = audioCtx.createMediaElementSource(audioElement);

1-3.AnalyserNodeを生成します。


app.js

    var analyser = audioCtx.createAnalyser();


1-4.AnalyserNodeをMediaElementSourceに紐づけます。


app.js

    audioSrc.connect(analyser);

audioSrc.connect(audioCtx.destination);

1-5.音楽データを格納する配列(Unit8Array)を用意し、データを突っ込みます。

配列の大きさが棒グラフのバーの数になるので見た目を確認しながら調整します。


app.js

var frequencyData = new Uint8Array(100);

analyser.getByteFrequencyData(frequencyData);

※app.jsでは、下記の処理はfunction renderChart()内に記述してます。

analyser.getByteFrequencyData(frequencyData);


2.D3.jsを使って音楽データを可視化する

続いてD3.jsを使って音楽データを可視化していきます。


D3.jsとは?

Wikipediaより引用:


D3.js(またはD3:Data-Driven Documents)は、 2011年に開発が始まったウェブブラウザで動的コンテンツを描画するJavaScriptライブラリである。 World Wide Web Consortium準拠のデータ可視化ツールとして、Scalable Vector Graphics(SVG)、JavaScript、HTML5、Cascading Style Sheetsを最大限に活用している。 その他多くのライブラリとは対照的に、最終的に出力された結果に視覚的な調整ができる。


可視化イメージやドキュメンテーションについては公式ウェブサイトをご覧ください。

image

色々できそうですね!

今回は音楽データを棒グラフで表現してみます!


手順

2-1.SVG(Scalable Vector Graphics)の高さ(svgHeight)と幅(svgWidth)のピクセルを定義します。


app.js

    var svgHeight ='600';

var svgWidth = '1000';

2-2.棒グラフの間隔(barPadding)を定義します。


app.js

    var barPadding = '5';


2-3.SVG(Scalable Vector Graphics)を生成します。


app.js

    function createSvg(parent, height, width) {

return d3.select(parent).append('svg').attr('height', height).attr('width', width);
}

2-4.SVG(Scalable Vector Graphics)の要素をgraphの値として格納します。


app.js

    var graph = createSvg('#graph', svgHeight, svgWidth);


2-5.D3チャートの初期設定を行います。先ほど定義したfrequencyDataをデータとして使います。

x, y, height, widthのうち、xとwidthは固定値になるので予め定義しておきます。xは横軸のポジションを示すので、それぞれのバーに割り当てられた値にsvgWidth / dataset.lengthを掛けた値とすることで均等間隔にします。イメージとしては、インデックス(i)が[0,1,2,3,...,99]と割り当てられるに対して、svgWidth / dataset.length → 10000 / 100 =10 を掛けることにより、[0,10,20,30,...,990]とすることで、svgWidthの端から端まで均等にバーを表示させます。width(棒グラフのバーの幅)はsvgWidthをfrequencyDataの長さで割った値から棒グラフの間隔を引いた値とします。


app.js

    graph.selectAll('rect')

.data(frequencyData)
.enter()
.append('rect')
.attr('x', function (d, i) {
return i * (svgWidth / frequencyData.length);
})
.attr('width', svgWidth / frequencyData.length - barPadding);

2-6.D3チャートを描写するための処理(rendarChart)を定義します。


app.js

    function renderChart() {

requestAnimationFrame(renderChart);

2-6.音楽データをfrequencyDataの配列に取り込みます。


app.js

analyser.getByteFrequencyData(frequencyData);


※1-5に記載の通り、app.jsでは、下記の処理はfunction renderChart()内に記述してます。この部分はWeb Audio APIの処理になります。

analyser.getByteFrequencyData(frequencyData);

2-7.D3チャートをfrequencyDataに取り込んだデータにてアップデートします。

xとwidthは定義済みなので、yとheightを定義します。yはsvgHeightからfrequencyDataに格納された値(d)に2を掛けた値を引いた値とします。D3チャートは左上から座標をカウントするので、svgHeightからfrequencyDataに格納された値(d)を引く必要があります。Heightは単純にfrequencyDataに格納された値(d)に2を掛けた値とします。(yとheightの「2」はチャートの見た目によって調整します。今回はsvgHeightが600ピクセルで、frequencyDataが取りうる値が0~255なので「2」で掛けてます。)また、RGBのGreenをfrequencyDataに格納された値(d)にすることで、データの大きさによって緑の濃さが変わるようにします。


app.js

       graph.selectAll('rect')

.data(frequencyData)
.attr('y', function(d) {
return svgHeight - (d * 2);
})
.attr('height', function(d) {
return d * 2;
})
.attr('fill', function(d) {
return 'rgb(0, ' + d + ', 0)';
});
}

2-8.D3チャート描写処理(rendarChart)を繰り返し実行します。


app.js

    renderChart();



まとめ

こんな感じのD3チャートを作成できました。もっと色々試してみたいですが、今日はここまで。

(下の写真をクリックするとStreamableの動画を観れます)

image

本投稿は下記のブログを参考にして作成しました。興味のある人はこちらのブログも確認してみてください。

https://www.bignerdranch.com/blog/music-visualization-with-d3-js/


余談

最後に、、、知ってる人も多いとは思いますが、クリスマス:santa:にピッタリな数式を紹介します!

数学が得意な人は是非解いてみてください!!

image

※答えはコメント欄をチェック。


フロムスクラッチ Advent Calendar 2016は以上です!ご購読ありがとうございました!!

Wish you a merry Xmas!!

End.