8147207268_99209ae25e_z
(写真:kimi kao)

以前、楽曲全体に現れる全ての音程を集計してみる記事を書きました。
(参照:楽曲中の和音に含まれる全ての音程を分析して表示するPHPコード)

これを応用すると楽曲全体の音程の緊張度の推移をグラフにすることができます。
今回はGoogle Chart API を使用することで、Excelでの作業を省略しつつ、美しいグラフで音楽を目で見てみるという試みです。




まず、今回使用するGoogle Chart APIについて。
Google Chart APIは、以下のようなグラフを簡単に作れてしまうサービスです。
googlechart_graphs

グラフを作成する方法は2種類あります。
  • 簡易版:HTMLのimgタグにパラメーターを書けば直接画像が出力されるタイプ。
  • 多機能版:Java script を使用して大規模なデータのグラフ化や様々なコントロールが可能なタイプ。

簡易版はHTMLがわかる人であれば非常に簡単に扱えるようにできています。
例えば、
<img src="http://chart.apis.google.com/chart&chs=200x200&chd=t:20,30,10,70,50&cht=lc">
このように書くだけで、
 

こんなグラフが表示されます。
とっても簡単です。
文法の詳細はオフィシャルサイトをご覧ください。
参照:Charts | Google Developers



というわけで、これを利用して楽曲の緊張度の推移をグラフにしてみます。

前提として、
pitchs
の順に、数値の大きいものほど緊張度が高いものとします。
い、異論は認めないんだから!

和声機能は排除して考えます。
複音程も排除です。



では、PHPプログラムのコードを紹介します。
以前のものをカスタマイズしたものとなります。
$chords = "c4e4g4c5e5,c4d4a4d5f5,b3d4g4d5f5,c4e4g4c5e5,c4e4a4e5a5,c4d4f+4a4d5,b3d4g4d5g5,b3c4e4g5c6,a3c4e4g4c5,d3a3d4f+4c5,g3a-3d4f4b4,e3g3c4g4c5,e3f3a3c4f4,d3f3a3c4f4,g2d3g3b3f4,c3e3g3c4e4,c3g3b-3c4e4,f2f3a3c4e4,f+2c3a3c4e-4,a-2f3b3c4d4,g2f3g3b3d4,g2e3g3c4e4,g2d3g3c4f4,g2d3g3b3f4,g2e-3a3c4f+4,g2e3g3c4g4,g2d3g3c4f4,g2d3g3b3f4,c2c3g3b-3e4,c2c3d3f3a3c4f4,c2b2d4f4g4b4d5f5,c2c3e4g4c5";
$array = explode(",", $chords);

$result = array("0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0");
$chart = array();
foreach ($array as $notes){
	if(preg_match_all("/[a-z+-]{1,2}\d{1,2}/",$notes,$note)){
		$count = count($note[0]);
	
			for ($i = 0; $i < $count - 1; $i++){

				for ($i2 = 0; $i2 < $count - $i - 1; $i2++){
					preg_match("/[a-z+-]{1,2}/",$note[0][$i2],$note_lower);
					if($note_lower[0] == "c"){$pitch_lower = 0;}
					if($note_lower[0] == "c+" || $note_lower[0] == "d-"){$pitch_lower = 1;}
					if($note_lower[0] == "d"){$pitch_lower = 2;}
					if($note_lower[0] == "d+" || $note_lower[0] == "e-"){$pitch_lower = 3;}
					if($note_lower[0] == "e"){$pitch_lower = 4;}
					if($note_lower[0] == "f"){$pitch_lower = 5;}
					if($note_lower[0] == "f+" || $note_lower[0] == "g-"){$pitch_lower = 6;}
					if($note_lower[0] == "g"){$pitch_lower = 7;}
					if($note_lower[0] == "g+" || $note_lower[0] == "a-"){$pitch_lower = 8;}
					if($note_lower[0] == "a"){$pitch_lower = 9;}
					if($note_lower[0] == "a+" || $note_lower[0] == "b-"){$pitch_lower = 10;}
					if($note_lower[0] == "b"){$pitch_lower = 11;}
					preg_match("/\d{1,2}/",$note[0][$i2],$register_lower);
					$register_lower = $register_lower[0] * 12 - 12;
					
					preg_match("/[a-z]/",$note[0][$i2 + $i + 1],$note_upper);
					if($note_upper[0] == "c"){$pitch_upper = 0;}
					if($note_upper[0] == "c+" || $note_upper[0] == "d-"){$pitch_upper = 1;}
					if($note_upper[0] == "d"){$pitch_upper = 2;}
					if($note_upper[0] == "d+" || $note_upper[0] == "e-"){$pitch_upper = 3;}
					if($note_upper[0] == "e"){$pitch_upper = 4;}
					if($note_upper[0] == "f"){$pitch_upper = 5;}
					if($note_upper[0] == "f+" || $note_upper[0] == "g-"){$pitch_upper = 6;}
					if($note_upper[0] == "g"){$pitch_upper = 7;}
					if($note_upper[0] == "g+" || $note_upper[0] == "a-"){$pitch_upper = 8;}
					if($note_upper[0] == "a"){$pitch_upper = 9;}
					if($note_upper[0] == "a+" || $note_upper[0] == "b-"){$pitch_upper = 10;}
					if($note_upper[0] == "b"){$pitch_upper = 11;}					
					preg_match("/\d{1,2}/",$note[0][$i2 + $i + 1],$register_upper);
					$register_upper = $register_upper[0] * 12 - 12;

					$register = ($pitch_upper + $register_upper) - ($pitch_lower + $register_lower);
					if($register > 12) {
						$register = $register - (12 * floor($register / 12));
					}

					for($i3 = 0;$i3 < 12;$i3++){
						if($register == 12){$register = 0;}
						if($register == $i3){$replacement = array($i3 => $result[$i3] + 1);
							$result = array_replace($result, $replacement);
						}
					}
				}
			}

			$sum = ($result[0] * 1) + ($result[1] * 11) + ($result[2] * 9) + ($result[3] * 6) + ($result[4] * 4) + ($result[5] * 3) + ($result[6] * 12) + ($result[7] * 2) + ($result[8] * 5) + ($result[9] * 7) + ($result[10] * 8) + ($result[11] * 10);
			$sum = $sum * 0.3;
			array_push($chart, $sum);
			$sum = 0;
			$result = array("0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0");
	}
}

$chart = implode(",", $chart);
echo $chart;
echo <<< EOF
<img src="http://chart.apis.google.com/chart?chs=640x300&chd=t:$chart&cht=lc">
EOF;
分析する作品は前回同様、バッハの平均律クラヴィーア曲集第1巻第1番の前奏曲です。
これを実行すると、

 

このように表示されます。
いかがでしょうか?
おおよそなだらかな流れの中で3回ほど緊張の波を経過し、最後にぐっと緊張が高まってスッと終わる。
そんな感じが見て取れます。
あくまでも縦の和音を分析対象としており、和声機能は排除されていますので、実際の聴取した感覚とは若干違いはあると思いますがおおよそこんなものでしょう。


続けて、スクリャービンの前奏曲 op.16 第5番も見てみましょう。

 

バッハに比べると、基本的に緊張度の高い響きで構成されているのがわかります。
面白いことに、楽曲の形式が明確に見て取れます。


このように、Google Chart APIを使えば、とても簡単に楽曲の雰囲気を目で見ることができます。



さて、個人的にこのグラフはあまりに味気なく思います。
やはり音楽の分析なのですからグラフ自体も美しくありたい!
というわけで、Google Chart APIの多機能版を使用して、さらに美しいグラフにしてみましょう。

PHPプログラムのコードは以下となります。
<?php
$chords = "c4e4g4c5e5,c4d4a4d5f5,b3d4g4d5f5,c4e4g4c5e5,c4e4a4e5a5,c4d4f+4a4d5,b3d4g4d5g5,b3c4e4g5c6,a3c4e4g4c5,d3a3d4f+4c5,g3a-3d4f4b4,e3g3c4g4c5,e3f3a3c4f4,d3f3a3c4f4,g2d3g3b3f4,c3e3g3c4e4,c3g3b-3c4e4,f2f3a3c4e4,f+2c3a3c4e-4,a-2f3b3c4d4,g2f3g3b3d4,g2e3g3c4e4,g2d3g3c4f4,g2d3g3b3f4,g2e-3a3c4f+4,g2e3g3c4g4,g2d3g3c4f4,g2d3g3b3f4,c2c3g3b-3e4,c2c3d3f3a3c4f4,c2b2d4f4g4b4d5f5,c2c3e4g4c5";
$array = explode(",", $chords);

$result = array("0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0");
$chart = array();
$x = array();
$y = array();
$size = array();
$bubble = array();
$xposition = count($array);
$xposition = 100 / $xposition;
$xposition = floor($xposition);
if($xposition <= 0){$xposition = 0.5;}

foreach ($array as $notes){
	if(preg_match_all("/[a-z+-]{1,2}\d{1,2}/",$notes,$note)){
		$count = count($note[0]);
	
			for ($i = 0; $i < $count - 1; $i++){

				for ($i2 = 0; $i2 < $count - $i - 1; $i2++){
					preg_match("/[a-z+-]{1,2}/",$note[0][$i2],$note_lower);
					if($note_lower[0] == "c"){$pitch_lower = 0;}
					if($note_lower[0] == "c+" || $note_lower[0] == "d-"){$pitch_lower = 1;}
					if($note_lower[0] == "d"){$pitch_lower = 2;}
					if($note_lower[0] == "d+" || $note_lower[0] == "e-"){$pitch_lower = 3;}
					if($note_lower[0] == "e"){$pitch_lower = 4;}
					if($note_lower[0] == "f"){$pitch_lower = 5;}
					if($note_lower[0] == "f+" || $note_lower[0] == "g-"){$pitch_lower = 6;}
					if($note_lower[0] == "g"){$pitch_lower = 7;}
					if($note_lower[0] == "g+" || $note_lower[0] == "a-"){$pitch_lower = 8;}
					if($note_lower[0] == "a"){$pitch_lower = 9;}
					if($note_lower[0] == "a+" || $note_lower[0] == "b-"){$pitch_lower = 10;}
					if($note_lower[0] == "b"){$pitch_lower = 11;}
					preg_match("/\d{1,2}/",$note[0][$i2],$register_lower);
					$register_lower = $register_lower[0] * 12 - 12;
					
					preg_match("/[a-z]/",$note[0][$i2 + $i + 1],$note_upper);
					if($note_upper[0] == "c"){$pitch_upper = 0;}
					if($note_upper[0] == "c+" || $note_upper[0] == "d-"){$pitch_upper = 1;}
					if($note_upper[0] == "d"){$pitch_upper = 2;}
					if($note_upper[0] == "d+" || $note_upper[0] == "e-"){$pitch_upper = 3;}
					if($note_upper[0] == "e"){$pitch_upper = 4;}
					if($note_upper[0] == "f"){$pitch_upper = 5;}
					if($note_upper[0] == "f+" || $note_upper[0] == "g-"){$pitch_upper = 6;}
					if($note_upper[0] == "g"){$pitch_upper = 7;}
					if($note_upper[0] == "g+" || $note_upper[0] == "a-"){$pitch_upper = 8;}
					if($note_upper[0] == "a"){$pitch_upper = 9;}
					if($note_upper[0] == "a+" || $note_upper[0] == "b-"){$pitch_upper = 10;}
					if($note_upper[0] == "b"){$pitch_upper = 11;}					
					preg_match("/\d{1,2}/",$note[0][$i2 + $i + 1],$register_upper);
					$register_upper = $register_upper[0] * 12 - 12;

					$register = ($pitch_upper + $register_upper) - ($pitch_lower + $register_lower);
					if($register > 12) {
						$register = $register - (12 * floor($register / 12));
					}

					for($i3 = 0;$i3 < 12;$i3++){
						if($register == 12){$register = 0;}
						if($register == $i3){$replacement = array($i3 => $result[$i3] + 1);
							$result = array_replace($result, $replacement);
						}
					}
				}
			}

			$sum = ($result[0] * 1) + ($result[1] * 11) + ($result[2] * 9) + ($result[3] * 6) + ($result[4] * 4) + ($result[5] * 3) + ($result[6] * 12) + ($result[7] * 2) + ($result[8] * 5) + ($result[9] * 7) + ($result[10] * 8) + ($result[11] * 10);
			$sum = $sum * 0.3;
			array_push($chart, $sum);
			
			$position_range = $sum / 12;
			$ix += 1;

			if($result[0]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 1 . ", 'オクターブ', " . $result[0] . "]");}
			if($result[1]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 11 . ", '短2度', " . $result[1] . "]");}
			if($result[2]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 9 . ", '長2度', " . $result[2] . "]");}
			if($result[3]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 6 . ", '短3度', " . $result[3] . "]");}
			if($result[4]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 4 . ", '長3度', " . $result[4] . "]");}
			if($result[5]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 3 . ", '完全4度', " . $result[5] . "]");}
			if($result[6]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 12 . ", '増4度', " . $result[6] . "]");}
			if($result[7]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 2 . ", '完全5度', " . $result[7] . "]");}
			if($result[8]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range . ", '短6度', " . $result[8] . "]");}
			if($result[9]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 7 . ", '長6度', " . $result[9] . "]");}
			if($result[10]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range * 8 . ", '短7度', " . $result[10] . "]");}
			if($result[11]){array_push($bubble, "['', " . $ix * $xposition . ", " . $position_range . ", '長7度', " . $result[11] . "]");}

			$sum = 0;
			$result = array("0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0");
	}
}

?>

<html>
  <head>
  
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
    google.load("visualization", "1", {packages:["corechart"]});
    google.setOnLoadCallback(drawSeriesChart);

    function drawSeriesChart() {

      var data = google.visualization.arrayToDataTable([
	  	['ID', 'Life Expectancy', 'Fertility Rate', 'Region',     'Population'],
		
      ]);

      var options = {
        //title: 'Correlation between life expectancy, fertility rate and population of some world countries (2010)',
        hAxis: {textPosition: 'none',
				gridlines: {
					color: 'none'
				},
				baselineColor: 'none'
		},
        vAxis: {textPosition: 'none',
				gridlines: {
					color: 'none'
				},
				baselineColor: 'none'
		},
		colors: ['#1D2088','#8FC31F','#009E96','#00A0E9','#0068B7','#009944','#8FC31F','#920783','#E4007F','#F39800','#E5004F','#E60012'],
		chartArea: {backgroundColor: '#EFF1F3'},
		tooltip: {trigger: 'none'},
        bubble: {
			textStyle: {fontSize: 11},
			stroke: "none",
			opacity: 0.5
		}
      };

      var chart = new google.visualization.BubbleChart(document.getElementById('series_chart_div'));
      chart.draw(data, options);
    }
    </script>
  </head>
  <body>
    <div  id="series_chart_div" style="width: 100%; height: 100%;"></div>
  </body>
</html>

さて、このコードでバッハを見てみると以下のようになります。
bubble-bach
おお!
美しいです!

円の縦の並びが一つの和音を表しています。
また、その高さが緊張度です。
先の折れ線グラフと同じ流れが見えますね。
さらに、各音程ごとに色を変えてあり、また、円の大きさがその音程の含まれる量を表しています。


これをスクリャービンでも見てみましょう。
bubble-scriabin
お花畑みたい。
かわいい。


というわけで、いかがでしたでしょうか?
今回のは、もしかしたら誰かの役に立つような気がしないでもないです。