見出し画像

Processingを使ったビジュアルプログラミング

はじめに

今回は、Processingというプログラミング開発環境で音とグラフィックを同期させたシステムを作りたいと思います。

Processingについて

Processingとは、MITメディアラボで開発されたビジュアルデザインをするための開発環境です。Javaのような言語で開発できます。 

作ってみたいもの

音楽データを認識して、音楽の音量や周波数に応じて表示が変化する、イコライザーの様なものに挑戦します。

音楽データはローカルにあるmp3を再生して、この音楽データに同期させます。イコライザー的なものを作るには、音楽データを高速フーリエ変換して周波数ごとに分けて、グラフ表示するなどで実現できそうです。

以下のサイトを参考にしました。

プログラミング

Minimライブラリをインストール

Processingで音楽を扱うにはMinimライブラリを使います。
デフォルトではMinimライブラリがインストールされていないので、まずインストールから始めます。

Minimライブラリのインポート

まずは、以下の2行のimportを行います。これをしないとMinimライブラリが利用できません。

import ddf.minim.*;
import ddf.minim.analysis.*;

mp3を再生

音楽を再生するにはAudioPlayerクラスを利用します。playerというのがAudioPlayerクラスのオブジェクトで、MinimクラスのオブジェクトであるminimのloadFile関数で再生したいmp3ファイルロードします。その後にplay関数で再生できます。

minim = new Minim(this);
player = minim.loadFile("ファイル名.mp3"); 
player.play();

次に高速フーリエ変換するための準備です。ProcessingではFFTクラスを利用すれば、私でも簡単にできそうです。

  fft = new FFT(player.bufferSize(), player.sampleRate());
  specSize = (int)fft.specSize();

FFTクラスのspecSize関数で音楽データを周波数ごとに何分割するかを返します。私の環境では513を返しました。以下のようなイメージです。

音量は513分割ごとにそれぞれ以下の方法で取得できます。

fft.forward(player.right); /* leftにすれば左側、mixにすれば左右混合 */

float band = fft.getBand(i) /* iに0~512のインデックを入力 */

上記のbandに入っているデータでグラフを表示すれば、イコライザー表示ができそうです。

今回はこのグラフを横方向一列に並べるのではなく、円状になるように並べます。これをステレオの左右でそれぞれ描く予定です。
さらに円状のグラフだけでは面白くないので、背景色を音量に応じて変えられるようにします。

背景色を変える

色設定はRGBとHSVのどちらかを選べるので、HSVで設定を行います。
HSVは以下のように0~360の数値で色を表すことができます。

ソースコードは以下になります。

colorMode(HSB, 360, 100, 100); /* HSV(HSB)設定 */

fill(band, 100, 100);
rect(0, 0, 800, 800);

HSB設定後に、音量データをfill関数の第1引数に入れています。この第1引数がHSVの0~360の値になり色指定です。最後にrect関数で画面全体を指定した色で塗りつぶしています。

ソースコード全体

100行程度で書けました。こんな簡単にできるのですね。

import ddf.minim.*;
import ddf.minim.analysis.*;

Minim minim;
AudioPlayer player;
FFT fft;
int specSize;
float[] band_left;
float[] band_right;

void setup()
{
  frameRate(10);
  
  size(800, 800);
  
  colorMode(HSB, 360, 100, 100);
  
  minim = new Minim(this);
  player = minim.loadFile("Zight - Number One (EDM _ Instrumental).mp3");
  
  fft = new FFT(player.bufferSize(), player.sampleRate());
  specSize = (int)fft.specSize();

  band_left = new float[specSize];
  band_right = new float[specSize];
    
  fft.window(FFT.HANN);
  
  player.play();
}

void draw()
{
  float max_band;

  if(player.isPlaying() == false)
  {
/*    saveFrame("frames/####.tif"); */
    return;
  }

  stroke(255);
 
  float max_band_left = get_band_left();  
  float max_band_right = get_band_right();  
  
  if(max_band_left > max_band_right)
  {
    max_band = max_band_left;
  }
  else
  {
    max_band = max_band_right;  
  }
  
  fill(max_band, 100, 100);
  rect(0, 0, 800, 800);
 
  draw_spec(band_left, (int)(800 * 0.25), (int)(800 * 0.5));
  draw_spec(band_right, (int)(800 * 0.75), (int)(800 * 0.5));
  
/*  saveFrame("frames/####.tif"); */
}

float get_band_left()
{
  float max_band = 0;
  
  fft.forward(player.left);
 
  for (int i = 0; i < specSize; i++)
  {
    band_left[i] =  fft.getBand(i);   

    if(max_band < band_left[i])
    {
      max_band = band_left[i];
    }
  }

  return max_band;
}

float get_band_right()
{
  float max_band = 0;
  
  fft.forward(player.right);
 
  for (int i = 0; i < specSize; i++)
  {
    band_right[i] =  fft.getBand(i);   

    if(max_band < band_right[i])
    {
      max_band = band_right[i];
    }
  }

  return max_band;
}

void draw_spec(float band[], int x, int y)
{
  for(int i = 0; i < specSize; i++)
  {
    float theta = map(i, 0, specSize, 0, 360);

    line(x, y, x + band[i] * 3 * cos(theta), y + band[i] * 3 * sin(theta));
  }
}

void stop()
{
  player.close();
  minim.stop();
  
  super.stop();
}

完成品

動画にする都合で、サイズをだいぶ小さくしています。

この記事が気に入ったらサポートをしてみませんか?