サウンドの基本(minimライブラリの使用)

このページでは、Processingで音を扱うためのライブラリMinimを使います。Minimを使うと、音声ファイルの再生、効果音、マイク入力、波形の表示、サイン波の生成などを短いコードで試すことができます。

Minimの詳しい情報やサンプルは、以下のページから確認できます。
https://github.com/ddf/Minim

以下のサンプルでは、Processingのスケッチフォルダの中にdataフォルダを作り、その中に音声ファイルを入れて使います。たとえばsample.mp3を使う場合は、data/sample.mp3になるように配置してください。

音楽ファイルの再生

まずは、音声ファイルを読み込んで再生する基本のサンプルです。以下の音声ファイルをダウンロードして、スケッチのdataフォルダに入れてください。
sample.mp3 (右クリックでダウンロード)

リスト1
import ddf.minim.*;

Minim minim;          // Minim全体を管理するための変数
AudioPlayer player;  // 音声ファイルを再生するための変数

void setup() {
  size(400, 200);

  // Minimを使い始めるための初期化
  minim = new Minim(this);

  // dataフォルダの中にあるsample.mp3を読み込む
  player = minim.loadFile("sample.mp3");

  // 音声ファイルを再生する
  player.play();
}

void draw() {
  background(30);

  fill(255);
  textAlign(CENTER, CENTER);
  text("sample.mp3を再生中", width / 2, height / 2);
}

void stop() {
  // プログラム終了時に音声ファイルを閉じる
  player.close();

  // Minimを終了する
  minim.stop();

  // Processing本来の終了処理を実行する
  super.stop();
}

ボタンに音をつける

次は、マウスで四角形をクリックしたときに効果音を鳴らします。以下の音声ファイルをdataフォルダに入れてください。
button01a (右クリックでダウンロード)

フリーの効果音は、以下のようなサイトから探すこともできます。
http://taira-komori.jpn.org/game01.html

リスト2
import ddf.minim.*;

Minim minim;
AudioPlayer buttonSound;

boolean isPressed = false;  // ボタンが押されているかどうか

void setup() {
  size(400, 400);

  minim = new Minim(this);

  // dataフォルダ内のbutton01a.mp3を読み込む
  buttonSound = minim.loadFile("button01a.mp3");

  rectMode(CENTER);
  textAlign(CENTER, CENTER);
}

void draw() {
  background(255);

  // isPressedがtrueなら赤、falseなら緑でボタンを描く
  if (isPressed) {
    fill(255, 80, 80);
  } else {
    fill(80, 220, 120);
  }

  // 画面中央に四角形のボタンを描く
  rect(width / 2, height / 2, 160, 160, 12);

  // ボタンの上に文字を描く
  fill(0);
  text("click", width / 2, height / 2);
}

void mousePressed() {
  // マウスが四角形の中にあるかを調べる
  boolean insideButton =
    mouseX > width / 2 - 80 &&
    mouseX < width / 2 + 80 &&
    mouseY > height / 2 - 80 &&
    mouseY < height / 2 + 80;

  if (insideButton) {
    isPressed = true;

    // 連続でクリックしても最初から鳴るように、再生位置を先頭に戻す
    buttonSound.rewind();
    buttonSound.play();
  }
}

void mouseReleased() {
  isPressed = false;
}

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

音とは一体何か

音の正体は振動です。物が振動すると、その振動が空気に伝わり、耳の鼓膜を振動させます。つまり、音を聞くということは、振動を知覚するということです。

参考サイト
http://poem.kagebo-shi.com/audio1/a01.html

波形とは

音は波として表すことができます。次のサンプルでは、音楽ファイルを再生しながら、左右の音声チャンネルの波形を画面に描きます。

リスト3
import ddf.minim.*;

Minim minim;
AudioPlayer player;

float waveH = 100;  // 波形を上下にどれくらい大きく描くか

void setup() {
  size(512, 240);

  minim = new Minim(this);

  // 第2引数の512は、波形を取り出すためのバッファサイズ
  player = minim.loadFile("sample.mp3", 512);

  // 音声ファイルを再生する
  player.play();
}

void draw() {
  background(0);

  // 波形を白い線で描く
  stroke(255);

  //波形を描く
  //left.get()とright.get()が返す値は-1から+1なので、見やすくするために、
  //waveH(初期値は100)を掛けている。
  //サウンドファイルがモノラルの場合は、left.get()とright.get()が返す値は同じ。  
  for(int i = 0; i < player.left.size()-1; i++){

    point(i, 60 + player.left.get(i)*waveH);  //左の音声の波形を画面上に描く
    point(i, 180 + player.right.get(i)*waveH);  //右  〃
  }

  fill(255);
  text("Left", 10, 55);
  text("Right", 10, 175);
}

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

いろいろな音声ファイルを試してみてください。mp3以外にも、wav、aiff、auなどの形式を扱えます。

縦方向の大きさは振幅、つまり音の大きさに関係します。横方向の波の細かさは周波数、つまり音の高さに関係します。

音の3要素

音には大きさ高さ音色の3つの要素があります。大きさは振幅、高さは周波数に関係します。音色は、複数の周波数がどのように混ざっているかによって変化します。

音色

きれいなサイン波のような音は純音と呼ばれます。一方、声や楽器の音のように複数の周波数が混ざった音は複合音と呼ばれます。
sin00900.AIF

マイクからの入力

次はマイク入力の波形を表示します。ノートパソコンでは内蔵マイクを使えることが多いですが、デスクトップ環境では別途マイクが必要になる場合があります。Macの場合は、システム設定でProcessingにマイクの使用を許可しておく必要があります。

リスト4
import ddf.minim.*;

Minim minim;
AudioInput in;  // マイク入力を扱うための変数

float waveH = 180;  // 波形を見やすくするための倍率

void setup() {
  size(512, 240);

  minim = new Minim(this);

  // マイク入力を開始する
  // 内蔵マイクはMONOの方が安定しやすい
  in = minim.getLineIn(Minim.MONO, 512);
}

void draw() {
  background(0);

  // マイクから入ってきた音の波形を白で描く
  stroke(255);

  // input.mixは、マイク入力の音声データ
  // MONO入力の場合も、input.mixを使うと扱いやすい
  AudioBuffer buffer = in.mix;

  // bufferには、現在の音の波形データが入っている
  // buffer.size()は、そのデータの数
  //波形を描く
  //left.get()とright.get()が返す値は-1から+1なので、見やすくするために、
  //waveH(初期値は100)を掛けている。
  for(int i = 0; i < in.bufferSize()-1; i++){

    point(i, 60 + buffer.get(i)*waveH);  //左の音声の波形を画面上に描く
    point(i, 180 + buffer.get(i)*waveH);  //右  〃
  }

  // 現在のマイク入力の音量を取得する
  float level = in.mix.level();

  // 文字情報を表示する
  fill(255);
  text("Mic Input", 10, 20);
  text("level: " + nf(level, 1, 4), 10, 40);
}

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

口笛、拍手、声、机を叩く音などを入力して、波形の違いを観察してみましょう。levelの値が0.0000のまま変わらない場合は、マイクの権限や入力デバイスの設定を確認してください。

音の波形の生成

次は、Processingでサイン波を作って出力します。ここではMinimのOscilを使います。サイン波は、音の基本を理解するために重要な波形です。

mySineWaveSignalScreenSnapz002
リスト5
import ddf.minim.*;
import ddf.minim.ugens.*;

Minim minim;
AudioOutput out;  // 音を出力するための変数
Oscil sine;       // サイン波を作るための変数

float waveH = 80;

void setup() {
  size(512, 240);

  minim = new Minim(this);

  // ステレオの音声出力を準備する
  out = minim.getLineOut(Minim.STEREO, 512);

  // 440Hz、音量0.5、波形はサイン波でOscilを作る
  sine = new Oscil(440, 0.5, Waves.SINE);

  // 作ったサイン波を音声出力につなぐ
  sine.patch(out);
}

void draw() {
  background(0);

  // 出力されている音の波形を白で描く
  stroke(255);

  // 波形を描く
  for(int i = 0; i < out.bufferSize()-1; i++)
  {
    point(i, 60 + out.left.get(i)*waveH);  //左の音声の波形を画面上に描く
    point(i, 180 + out.right.get(i)*waveH);  //右  〃
  }

  fill(255);
  text("Sine Wave 440Hz", 10, 20);
}

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

音の波形の合成

複数のサイン波を同時に出力すると、波形が合成されます。ここでは440Hzと1000Hzの2つのサイン波を重ねます。

リスト6
import ddf.minim.*;
import ddf.minim.ugens.*;

Minim minim;
AudioOutput out;

Oscil sine1;  // 1つ目のサイン波
Oscil sine2;  // 2つ目のサイン波

float waveH = 80;

void setup() {
  size(512, 240);

  minim = new Minim(this);
  out = minim.getLineOut(Minim.STEREO, 512);

  // 440Hzのサイン波。音量は0.4
  sine1 = new Oscil(440, 0.4, Waves.SINE);

  // 1000Hzのサイン波。音量は0.2
  sine2 = new Oscil(1000, 0.2, Waves.SINE);

  // 2つの波を同じ出力につなぐと、自動的に合成される
  sine1.patch(out);
  sine2.patch(out);
}

void draw() {
  background(255);

  // 合成された波形を黒で描く
  stroke(0);

  // 波形を描く
  for(int i = 0; i < out.bufferSize()-1; i++)
  {
    point(i, 60 + out.left.get(i)*waveH);  //左の音声の波形を画面上に描く
    point(i, 180 + out.right.get(i)*waveH);  //右  〃
  }

  fill(0);
  text("440Hz + 1000Hz", 10, 20);
}

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

音の波形を再生中に変更

次は、マウスの位置で音を変化させます。マウスの横位置で周波数、縦位置で音量を変えます。

リスト7
import ddf.minim.*;
import ddf.minim.ugens.*;

Minim minim;
AudioOutput out;
Oscil sine;

float waveH = 80;

void setup() {
  size(512, 240);

  minim = new Minim(this);
  out = minim.getLineOut(Minim.STEREO, 512);

  sine = new Oscil(440, 0.3, Waves.SINE);
  sine.patch(out);
}

void draw() {
  background(0);

  // マウスの横位置を100〜1000Hzに変換する
  // 左に行くほど低い音、右に行くほど高い音になる
  float freq = map(mouseX, 0, width, 100, 1000);

  // マウスの縦位置を0.8〜0.0に変換する
  // 上に行くほど音が大きく、下に行くほど小さくなる
  float amp = map(mouseY, 0, height, 0.8, 0.0);

  // 変換した値をサイン波に設定する
  sine.setFrequency(freq);
  sine.setAmplitude(amp);

  // 現在出力されている波形を描く
  stroke(255);

  // 波形を描く
  for(int i = 0; i < out.bufferSize()-1; i++)
  {
    point(i, 60 + out.left.get(i)*waveH);  //左の音声の波形を画面上に描く
    point(i, 180 + out.right.get(i)*waveH);  //右  〃
  }

  fill(255);
  text("mouseX: pitch / mouseY: volume", 10, 20);
  text("freq: " + int(freq) + " Hz", 10, 40);
  text("amp: " + nf(amp, 1, 2), 10, 60);
}

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

簡単な音のビジュアライザー

最後に、音楽ファイルの音量に合わせて円の大きさを変える簡単なビジュアライザーを作ります。波形をそのまま描くだけでなく、音の大きさを図形の変化に使うと、映像表現に応用しやすくなります。

リスト8
import ddf.minim.*;

Minim minim;
AudioPlayer player;

void setup() {
  size(400, 400);

  minim = new Minim(this);
  player = minim.loadFile("sample.mp3", 512);

  // 音楽を繰り返し再生する
  player.loop();

  noStroke();
}

void draw() {
  // 背景を完全に消さず、少し残像が残るようにする
  background(0, 30);

  // 左右チャンネルの現在の音量を取得する
  float leftLevel = player.left.level();
  float rightLevel = player.right.level();

  // 左右の音量の平均を求める
  float level = (leftLevel + rightLevel) / 2;

  // 音量を円の大きさに変換する
  // 音量は小さい値になりやすいので、0〜0.5の範囲を使う
  float circleSize = map(level, 0, 0.5, 40, 360);

  // 円が大きくなりすぎないように制限する
  circleSize = constrain(circleSize, 40, 360);

  // 音量に合わせて色も少し変える
  float blueValue = map(level, 0, 0.5, 120, 255);
  blueValue = constrain(blueValue, 120, 255);

  // 音量に反応する円を描く
  fill(80, 160, blueValue, 180);
  ellipse(width / 2, height / 2, circleSize, circleSize);

  // 文字情報を表示する
  fill(255);
  textAlign(CENTER, CENTER);
  text("sound visualizer", width / 2, height - 30);
  text("level: " + nf(level, 1, 4), width / 2, height - 50);
}

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

音を使った表現では、再生、入力、波形、音量、周波数をそれぞれ別々に理解しておくと応用しやすくなります。まずはサンプルの数値を少しずつ変えて、音と画面の変化の関係を確認してみましょう。