こんにちは。
COVID-19(コロナ)禍の中、みなさん何事もなくおすごしでしょうか。
日本はじめ世界中のみなさんが平和に暮らせるよう祈っています。
とはいえ、自粛に疲れてきた方も多いと思うので、今日は自粛中でも家で遊べるプログミングの紹介をしたいと思います。
Processingというクリエイティブコーディング用のライブラリを使用して、自作のサウンドトラックを可視化してみます。
Processingはとてもシンプルで分かりやすく設計されているので、プログラミングに詳しくない人でもとっつきやすくておすすめです。
ダウンロードすると専用のエディタがあり、再生ボタンがついているので、コーディングして再生ボタンを押すとすぐに結果を確認できます。
言語もjava,javascript,pythonなどに対応しています。
自粛やリモートに少し飽きたり、疲れたりしてきた方も、Processingであらためてプログラミングの楽しさに触れて、心をリフレッシュしましょう。
はじめに、今日紹介するコードの描画結果をYoutubeに挙げておきました
たった数十行でこんなのができるなんて素晴らしいですね。
これはフーリエ変換という演算を使って、音を可視化してみたものです。
フーリエ変換ってなにやら難しそうですが、心配ありません。
少し説明を読んで、コードを書いて描画してみたら、何をやっているのかなんとなく理解できると思います。
それでは、実際にVJ体験を始めて行きます。
※VJ=ビデオジョッキー(Video jockey)やビジュアルジョッキー(visual jockey)のこと
参考URL:https://block.fm/news/TourMillenniumFalconDonaldGlover
フーリエ変換で音を可視化
音は波です。
波といっても海面の波のイメージとは少し違います。”疎密波”といって、空気の密度が小さい部分と大きい部分で構成された波です。
そして現実の世界には大きさや速度が異なる波がたくさん入り混じって存在しています。
波の速度を周波数といいます。
1秒に1回しか来ないゆっくりな波と、1秒に100000回来る速い波。ゆっくりな方を周波数が低い、速い方を周波数が高いと表現し、周波数が高いほど高い音に聴こえます。
とても大雑把に言うと、フーリエ変換は波を速さによって(周波数帯によって)分解してくれます。
ゆっくりな波はこのくらいの量、中くらいの波はこのくらいの量、速い波はこのくらいの量・・・と言う風に。
Processingには、このフーリエ変換を行うライブラリがデフォルトで存在するので、それを使って音を可視化してみたいと思います。
周波数帯ごとの分布を可視化
まず周波数ごとにどのくらい波が存在するのかを単純に線の高さで可視化してみたいと思います。
Processing3以降、モードの追加というメニューからGUIでライブラリをインストールできるので、Soundライブラリをインストールします。
そして以下のようなコードを記述して、実行するだけです。とっても楽ちん。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// Soundライブラリーの読み込み import processing.sound.*; // サウンドプレイヤー SoundFile soundfile; // オーディオデバイス AudioDevice myAudioServer; // FFT(高速フーリエ変換) FFT fft; // 周波数を何個に区切って分析するか(2の累乗である必要がある) int bands = 1024; // グラフの高さのスケールを設定 float scale = 20.0; void setup() { size(800, 600); fill(0, 127, 255); noStroke(); // オーディオバッファーの設定 myAudioServer = new AudioDevice(this, 44000, bands); // サウンドファイルを読み込んでプレイヤーを初期化 soundfile = new SoundFile(this, "hottoke.mp3"); // ループ再生 soundfile.loop(); // FFTの初期化 fft = new FFT(this, bands); fft.input(soundfile); } void draw() { background(0); // FFT解析実行 fft.analyze(); noFill(); stroke(255); // 線分の描画開始 beginShape(); // FFTのバンドの数だけくりかえし for (int i = 0; i < bands; i++) { // FFTの解析結果を高さに比例させてグラフを描く。 // 画面幅を周波数帯の数で割ることで、周波数ごとの横位置を指定する。 vertex(i * width/float(bands), height - fft.spectrum[i] * height * scale); } // 線分の描画終了 endShape(); } |
Processingの実行結果はsketchと呼ばれます。htmlのcanvasみたいな感じです。
まずsetup()関数で描画の設定をします。
setup()関数は起動時に一度だけ実行されます。
次にdraw()関数で描画を行います。
draw()は指定した間隔で繰り返し実行されます。アニメーションのコマみたいなものです。
fps(frame per second)、つまり1秒間に何コマ描画するかはsetup()内で以下のようにすれば設定できます。
1 2 |
// 一秒間に60コマ frameRate(60); |
setup()関数内では、塗りつぶしの設定や使用するライブラリの初期化、SoundFileの読み込みなど、一回だけ実行すれば良い処理を行います。
そしてdraw()関数内に一コマ一コマの描画処理を記述します。
ここではFFTで解析した周波数帯ごとの波数に比例して高くなるような線分を描画しています。
vertexは頂点を指定して線分を描画する関数です。i番目の周波数帯の横位置と、i番目の周波数帯の波の強さ(FFT解析結果)を縦位置を頂点とした線分を描画しています。左ほど低い周波数帯となります。
周波数帯ごとに色の異なる円にしてみる
これだけだと面白くないので、周波数が大きくなる(高い音)ほど大きい円を描画するようにし、さらに色を付けてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
// Soundライブラリーの読み込み import processing.sound.*; // サウンドプレイヤー SoundFile soundfile; // FFT(高速フーリエ変換) FFT fft; // FFTサイズ(2の累乗で指定する必要あり) int bands = (int) Math.pow(2, 4); // 描画する周波数の領域(低い方から3/4のみ描画) float bandRange = (float) 3 / 4; void setup() { frameRate(20); size(1280, 720); // 最も明るい色のみを描画するモード blendMode(LIGHTEST); // 色設定をHSBに colorMode(HSB, 360, 100, 100, 100); noStroke(); // サウンドファイルを読み込んでプレイヤーを初期化 soundfile = new SoundFile(this, "hottoke.mp3"); // ループ再生 soundfile.loop(); // FFTの初期化 fft = new FFT(this, bands); fft.input(soundfile); } void draw() { background(0); // 円の最小半径 float smallestD = height * 0.2; // 円の最大半径 float largestD = height; fft.analyze(); float spectrum = 0; float bandsForRender = bands * bandRange; int i; for (i = 0; i < bandsForRender; i++) { spectrum = fft.spectrum[i]; // 色の指定。周波数帯の順番であるiを、0から180に換算している。 float hue = map(i, 0, bandsForRender, 0, 180); fill(hue, map(spectrum, 0.01, 0.1, 33, 100), map(spectrum, 0.01, 0.1, 33, 100), 50); float diameter = map(i, 0.0, bandsForRender, smallestD, largestD); ellipse(width/2, height/2, diameter, diameter); } } |
さきほどは、周波数帯のループの中で線分を描画しましたが、今回は円を描画しています。
円を描画する前に、周波数帯の順番に比例した色と、その周波数帯の波の強さに比例した彩度と明度を指定しています。
setup()でcolorModeをHSBに設定してあるので、色の指定は0から360で指定できます。0もしくは360に近いほど赤く、180に近いほど青くなります。
map()は、縮尺を調整してくれる関数です。第1引数の値が、第2引数〜第3引数のどの割合にあたるかを、第4引数〜第5引数の割合に換算してくれます。ここでは、0から32(周波数帯の数 = Math.pow(2, 4))におけるiを、0から180に置き換えたらどうなるのかを計算してくれています。
これで低い周波数帯ほど赤く、高い周波数帯ほど青くなります。
さらに彩度と明度を、その周波数帯の波の強さに比例させています([0.01から0.1]におけるspectrumを[33から100]に換算している)。
これで強い周波数ほど彩度と明度が強くなります。
あらためて描画結果です。
低周波の音が多いときは真ん中の赤めの色メイン、高周波の音が鳴ると外側の青めの色まで彩度と明度が上がり、発光しているように見えると思います。
最後に
今回は基礎的な内容でしたが、Processingには他にもたくさん面白いライブラリやメソッドがあります。
音声だけでなく動画をリアクティブに加工したり、kinectと連携してモーショングラフィックを作ったりもできます。
Processingは遊びながらプログラムと描画の関係の感覚を身につけるにはもってこいだと思います。
僕は細かいグラフ描画などで、canvasをいじったりするときにProcessingで身に付けた感覚がとても役に立ちました。
コロナで暇な人はぜひ遊んでみてください。
*
GMOリサーチでは、WEBエンジニア(サーバーサイド、インフラ、フロントエンド)を随時募集しております。
興味のある方は、ぜひこちらからご応募ください!
詳しい募集要項など採用情報はこちら