CGプログラミング演習
7 音の入出力と視覚化

ここまで画像、映像の処理方法(入出力方法)を学んできました。
次に取り扱うのは「音」です。

1 音の視覚化

1.1 視覚化とは

  • 「視覚化」とは、基本的に「見えないものを、見えるようにする」ことをいいます。
  • ここまでの授業では、基本的に画像の解析結果をもとにしたグラフィックをつくってきました。
  • これは「見えているものを、別なかたちに変換する」ことであったと言えます。
  • 残る授業期間では、見えないものを見えるようにすることを、音を素材にして体験していきます。

1.2 Processing を利用した音の視覚化例

  • 音の視覚化は、古くからあるテーマのひとつで、多くの人々がこのテーマを追求してきました。もっとも古い例は 18世紀に見ることができるそうです。
  • Processing は、というよりもプログラミングは、音を視覚化するツールとしても有効です。
  • 以下、いくつかの事例を紹介します。

左上から順に、
- Karl Kliem: Berlin, 2005. http://vimeo.com/4475410
- Robert Hodgin (flight 404): Solar reworks, 2007. http://roberthodgin.com/solar-rework/
- Quayola: Partitura 001, 2011. http://www.quayola.com/soundvisualisation/partitura/
- iTunes の各種ビジュアライザ

1.3 サイマティクスの紹介

  • 見えないものを見えるようにするというのは、おもしろいテーマです。
  • このテーマには、人工分布などの統計情報を視覚化(グラフ化/ダイアグラム化)することもはいってきます。
  • が、やはり「音」というのが、人間にとっては目に見えないもののうちで最たるものと言えるのではないでしょうか。
  • Processing とは直接関係しませんが、おもしろいビデオを見つけたので紹介します。
  • 「サイマティクス」という音の視覚化手法を紹介したビデオです。
  • サイマティクスが描く幾何学図形は「クラドニ図形」と呼ばれます。
  • 物体と同様に、音にも「質感」があるのかもしれません。

2 Processing で音を扱うには

2.1 Minim ライブラリを利用する

  • 音に関する様々な機能が利用できる
  • サウンドの再生………WAV, AIFF, AU, SND, MP3形式のサウンドファイルを読み込んで再生
  • 録音………入力された音を、ディスクに直接、またはメモリー上のバッファーに録音可能
  • オーディオ入力………モノラル、ステレオのオーディオ入力
  • オーディオ出力………モノラル、ステレオでサウンドを出力
  • 音響合成………シンプルな音響合成のための機能
  • エフェクト………サウンドエフェクトのためのインターフェース
  • FFT………リアルタイムにスペクトル解析が可能

2.2 Minim ライブラリを利用するには

  • 新規プロジェクトで Minim ライブラリを利用するには、Processing のアプリケーションメニューから「Sketch」→「Import Library...」→「Minim Audio」を選択します。
  • すると、プログラムの冒頭に下記の import 文(ライブラリを導入するための命令文)が挿入されます。
import ddf.minim.*;
import ddf.minim.signals.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;

3 音を波形として扱う

  • 音とは空気を伝わる周波数です。周波数は、波形として表現することができます。
  • 波形を表現するための、もっとも一般的な方法は、サイン波(サインカーブ)を利用することです。
  • まずは、波形を表現するところからはじめてみましょう。

3.1 サイン波の生成

import ddf.minim.*;
import ddf.minim.signals.*;

// Minim ライブラリのインスタンス minim を定義
Minim minim;

// オーディオの出力先を変数 out として指定
AudioOutput out;

// サイン波の波形を sine に生成
SineWave sine;

// プログラム開始時の事前準備
void setup()
{
  // キャンバスサイズの指定
  size(600, 200);
  
  //Minim クラスを実体化
  minim = new Minim(this);
  
  // 出力先の変数 out にモノラルのオーディオ出力を指定
  out = minim.getLineOut(Minim.MONO);
  
  // 440 Hz、音量 1.0でサイン波を出力
  sine = new SineWave(440, 1.0, out.sampleRate());
  
  // ポルタメント(音程変化のなめらかさ)を200msecに
  sine.portamento(200);
  
  // 生成したサイン波をオーディオ出力に追加するように指定
  out.addSignal(sine);
}

// 描画内容を定める
void draw()
{
  // 背景色を指定
  background(0);
}

// マウスが動いた時の処理内容を定める
void mouseMoved()
{
  // マウスの X座標を周波数に設定する
  // X座標の(0, width)を、周波数(40, 2000)にマップ
  float freq = map(mouseX, 0, width, 40, 2000);
  
  sine.setFreq(freq);
  
  // マウスの Y座標を音量に設定
  // Y座標の(0, height)を、周波数(1.0, 0.0)にマップ
  float amp = map(mouseY, 0, height, 1.0, 0.0);
  
  sine.setAmp(amp);
}

// プログラム終了時の処理を定める
void stop()
{
  //プログラムの終了処理
  out.close();
  minim.stop();
  super.stop();
}

3.2 サイン波の視覚化

  • ステレオのサイン波を生成する
  • マウスのX座標で周波数を変化させる
  • マウスのY座標で音量を変化させる

import ddf.minim.*;
import ddf.minim.signals.*;

Minim minim;
AudioOutput out;
SineWave sine;

// プログラム開始時の事前準備
void setup()
{
  // キャンバスサイズの指定
  size(600, 200);

  // フレームレートの設定
  frameRate(60);
  
  // 描画結果をアンチエイリアシングする
  smooth();
  
  // 線幅を 2ピクセルに指定
  strokeWeight(2);

  // minim の初期化
  minim = new Minim(this);
  
  // 出力先の変数 out にモノラルのオーディオ出力を指定
  out = minim.getLineOut(Minim.MONO);
  
  // 440 Hz、音量 1.0でサイン波を出力
  sine = new SineWave(440, 1.0, out.sampleRate());
  
  // ポルタメント(音程変化のなめらかさ)を200msecに
  sine.portamento(200);
  
  // 生成したサイン波をオーディオ出力に追加するように指定
  out.addSignal(sine);
}

// 描画内容を定める
void draw()
{
  // 波形を表示
  background(0);
  stroke(0, 255, 0);

  // Y 座標の原点を画面の中心に移動
  translate(0, height/2);

  // バッファーに格納されたサンプル数だけくりかえし
  for (int i = 0; i < out.bufferSize() - 1; i++)
  {
    // サンプル数から、画面の幅いっぱいに波形を表示するようにマッピング
    float x = map(i, 0, out.bufferSize(), 0, width);

    // 画面の高さいっぱになるように、サンプルの値をマッピング
    float y = map(out.mix.get(i), 0, 1.0, 0, height/2);

    // 値をプロット
    point(x, y);
  }
}

// マウスが動いた時の処理内容を定める
void mouseMoved()
{
  // マウスの X座標を周波数に設定する
  // X座標の(0, width)を、周波数(40, 2000)にマップ
  float freq = map(mouseX, 0, width, 40, 2000);
  
  sine.setFreq(freq);
  
  // マウスの Y座標を音量に設定する
  // Y座標の(0, height)を、周波数(1.0, 0.0)にマップ
  float amp = map(mouseY, 0, height, 1.0, 0.0);

  sine.setAmp(amp);
}

// プログラム終了時の処理を定める
void stop()
{
  //プログラムの終了処理
  out.close();
  minim.stop();
  super.stop();
}

3.3 複数のサイン波をプロットする

  • 2種類のsin波を生成し、同時に鳴らす
  • 波形の表示を工夫してみる ‒ 1つめのSin波のレベルをX軸に、2つめのSin波のレベルをY軸にして波形をプロットとするとどうなるか?
  • リサージュ図形 ‒ 互いに直交する二つの単振動を順序対として得られる点の軌跡が描く平面図形

import ddf.minim.*;
import ddf.minim.signals.*;

Minim minim;
AudioOutput out;

// このプログラムで使用する 2つのサイン波を用意
SineWave sine1, sine2; 

// プログラム開始時の事前準備
void setup()
{
  // キャンバスサイズの指定
  size(600, 200);

  // フレームレートの設定
  frameRate(30);
  
  // 描画結果をアンチエイリアシングする
  smooth();
  
  // 線幅を 2ピクセルに指定
  strokeWeight(2);

  // minim の初期化
  minim = new Minim(this);

  // ステレオで出力する
  out = minim.getLineOut(Minim.STEREO);

  // 1 つめのサイン波を用意する
  sine1 = new SineWave(440, 0.8, out.sampleRate());
  sine1.portamento(200);

  // 左からの出力に割り当てる
  sine1.setPan(-1);

  // オーディオ出力に追加
  out.addSignal(sine1);

  // 2 つめのサイン波を用意し、
  sine2 = new SineWave(440, 0.8, out.sampleRate());
  sine2.portamento(200);

  // 右からの出力に割り当てる
  sine2.setPan(1);

  // オーディオ出力に追加
  out.addSignal(sine2);

  // 背景色を黒に設定
  background(0);
}

// 描画内容を定める
void draw()
{
  // 背景を黒にフェードさせる
  fill(0, 0, 0, 15);
  noStroke();
  rect(0, 0, width, height);

  // 色の設定
  noFill();
  stroke(0, 255, 0);

  // 画面の中心へ原点を移動
  translate(width/2, height/2);

  // 波形を描画する
  for (int i = 0; i < out.bufferSize() - 1; i++)
  {
    // X 座標を右チャンネンル、Y 座標を左チャンネルにする
    point(out.right.get(i) * width/2, out.left.get(i) * height/2);
  }
}

// マウスが動いた時の処理内容を定める
void mouseMoved()
{
  // マウスの X 座標を周波数に設定し、一方のサイン波に適用する
  // X座標の(0, width)を、周波数(40, 2000)にマップ
  float freq1 = map(mouseX, 0, width, 40, 2000);
  sine1.setFreq(freq1);

  // マウスの Y 座標を周波数に設定し、もう一方のサイン波に適用する
  float freq2 = map(mouseY, 0, height, 20, 1000);
  sine2.setFreq(freq2);
}

// プログラム終了時の処理を定める
void stop()
{
  //プログラムの終了処理
  out.close();
  minim.stop();
  super.stop();
}

4 音をスペクトルとして扱う

4.1 フーリエ変換によって、波形を解析する

  • 波形を凝視しても、聴こえる音を想像することは難しい
  • 「音色」を調べるには、音を「周波数成分(スペクトラム)」に分解して、各成分の強さを調べなくてはならない。
  • 分析結果を視覚化したものを「スペクトログラム」といい、これを生成するための装置を「ソノグラフ」という。
  • スペクトログラムは、声紋の鑑定や動物の鳴き声の分析などに用いられる。
    参考:スペクトログラム - Wikipedia

バイオリンのスペクトログラム。スペクトログラムとは、周波数を解析して、その内容を視覚化したもの。

  • ここでは、波形を数学的に分析する「フーリエ変換」を利用します。

4.2 Minim でのフーリエ変換(高速フーリエ変換 FFT: First Fourier Transform)の方法

  • FFT の使用準備(インスタンス生成)
FFT(int timeSize, float sampleRate)
  • FFTで波形を変換(周波数からスペクトラムへ)
forward(AudioBuffer buffer, int offset)
  • 指定した周波数帯の音量を取り出す
float getBand(int i)

4.3 基本的なサンプルプログラム(波形の棒グラフ化)

  • AudioInputからの入力信号をFFTして表示する
  • まずは単純な棒グラフで表示

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

Minim minim;

// オーディオ入力の変数を用意する
AudioInput in;

// FFTの変数を用意する
FFT fft;

// プログラム開始時の事前準備
void setup()
{
  // キャンバスサイズの指定
  size(1024, 300);

  // Minim の初期化
  minim = new Minim(this);

  // ステレオオーディオ入力を取得
  in = minim.getLineIn(Minim.STEREO, 512);

  // ステレオオーディオ入力を FFT と関連づける
  fft = new FFT(in.bufferSize(), in.sampleRate());
}

// 描画内容を定める
void draw()
{
  // 背景色を黒に設定
  background(0);
  
  // 線の色を白に設定
  stroke(255);

  // FFT 実行
  fft.forward(in.mix);

  // 棒グラフの描画
  for (int i = 0; i < fft.specSize(); i++)
  {
    float x = map(i, 0, fft.specSize(), 0, width);
    line(x, height, x, height - fft.getBand(i) * 8);
  }
}

// プログラム終了時の処理を定める
void stop()
{
  minim.stop();
  super.stop();
}