小学校教員のためのプログラミング入門
今回は音をいろいろ扱ってみるスケッチを作っていきます.
Processing で音を扱うためにはライブラリを利用することになります.Processing ではライブラリを利用することで,いろいろなことを行うことができるようになります.ライブラリについては http://processing.org/learning/libraries/ を見てください.
音を扱うためのライブラリには様々なものがありますが,標準でついてくる Minim というライブラリを使います.このライブラリを使うには,メニューから [Sketch] - [Import Library] - [minim] を選びます.スケッチコードの一行目に
import ddf.minim.*;
が挿入されれば準備完了です.
最初に音楽ファイルを読み込んで,その曲を演奏するスケッチを作ってみます.
演奏する音楽ファイルを準備してください.ファイル形式は wav や mp3 など様々な形式が扱えます.ファイルは画像のときと同じようにまずは作成しているスケッチの pde ファイルと同じ場所に保存してください.音楽ファイルがない場合は下のものを利用してください.
Minim ライブラリに含まれている様々な機能(メソッド)を利用するためには,まず Minim クラスのインスタンス(Minim オブジェクト)を生成します.なお,Minim オブジェクトはコード全体で利用するのでグローバル変数として宣言します.
Minim minim; void setup() { minim = new Minim( this ); }
minim オブジェクトの準備ができたら,メンバメソッドである loadFile を用いて音源ファイルを読み込みます.loadFIle の引数は音源ファイルのファイル名です.loadFile メソッドは AudioPlayer クラスのインスタンス(AudioPlayerオブジェクト)を戻しますので,それを変数に格納しておきます.この変数はコード全体で利用するのでグローバル変数として宣言します.一方,読み込みは準備的なことですので setup メソッド中でよいでしょう.
AudioPlayer song; : void setup() { : song = minim.loadFile( "moon_p.mp3" );
Minim クラス,AudioPlayer クラスのインスタンスを生成し利用した場合は,スケッチが終了するときに必ず後始末として,AudioPlayerクラスのメンバメソッド close,Minimクラスのメンバメソッド stop,および,super.stop を呼び出す必要があります.
これらのコードは,スケッチが開始するときに必ず呼び出される setup,実行中常に呼び出される draw と同じように用意されている,スケッチが終了するときに必ず呼び出される組み込みメソッド stop の中に書きます.
void stop() { song.close(); minim.stop(); super.stop(); }
読み込んだ音源を演奏するには,読み込んだときに生成された AudioPlayer オブジェクトに対してメンバメソッド play を呼び出します.今回は生成された AudioPlayer オブジェクトは song に格納されていますので,
song.play();
と書けば演奏が開始されます.
いつ演奏させるかについては,今回はキーボード上の P キーが押されたらとしてみます.キーボードが押されたときに呼び出されるメソッドは keyPressed です.また,どのキーが押されたかに関しては,組み込み変数 key に入力された文字が格納されますので,次のようにすることで,P キーが押されたときに演奏を開始することができます.
void keyPressed() { if( key == 'p' ) { song.play(); } }
以上からできあがるスケッチコードを次に示します.なお,draw メソッドの中身はありませんが,これがないとスケッチは終了してしまいますので,必ず書く必要があります.
import ddf.minim.*; Minim minim; AudioPlayer song; void setup() { minim = new Minim(this); song = minim.loadFile("moon_p.mp3"); } void draw() { } void keyPressed() { if ( key == 'p' ) { song.play(); } } void stop() { song.close(); minim.stop(); super.stop(); } |
なお,P キーが押されるたびに,演奏をしなおすようにしたいときは,play メソッドを呼び出す前に rewind メソッドを呼び出すと,頭出しをしてから演奏するという動きになります.一方,演奏終了後 rewind メソッドを呼び出さないと,play メソッドを呼び出しても曲の末尾から演奏を開始することになってしまいますので,再演奏を開始してくれませんので,注意してください.
効果音など短い音を鳴らす時には AudioPlayer クラスではなく,AudioSnippet クラスを利用します.このクラスを利用するためには,音源ファイルを読み込むときに Minim クラスの loadSnippet メソッドを利用します.
次に示す音源を鳴らすスケッチは,先のコードを次のように変更します.なお,play メソッドの前に rewind メソッドを入れておかないと一回しか音が出ないスケッチになってしまいます.
AudioPlayer song; → AudioSnippet song; song = minim.loadFile("moon_p.mp3"); → song = minim.loadSnippet("pi.mp3");
ここまでは音源ファイルを鳴らすということをしてきました.Processing では音を作って鳴らすことも簡単です.
下のスケッチは,440Hz の正弦波が作る音を発生するものです.
import ddf.minim.*; import ddf.minim.signals.*; Minim minim; AudioOutput out; SineWave sine; void setup() { minim = new Minim(this); out = minim.getLineOut(Minim.STEREO); sine = new SineWave(440, 0.5, out.sampleRate()); out.addSignal(sine); } void draw() { } void stop() { out.close(); minim.stop(); super.stop(); } |
音は空気の振動であることは知っていると思います.音の高低は空気の振動(波)の周波数で決まります.ドレミは周波数の違いということです.上のスケッチで出ている音のドレミの何であるかわかるでしょうか?音名はA,ハ長調ならラの音を鳴らしています.
SineWave メソッドは任意の周波数の正弦波を生成することができ,それを AudioOutput オブジェクトに addSignal メソッドで設定してあげることで,正弦波の音波を出すことができます.周波数を倍にすると 1 オクターブ上,半分にすると 1 オクターブ下になるので,
sine = new SineWave(440, 0.5, out.sampleRate());
の 440 の部分を 880 や 220 に変えると,1 オクターブ上と下のラの音が聞こえるはずです.
ピアノのラもハーモニカのラも周波数は同じです.では,なぜ聞こえる音が違うのかというと,音波の形状(波形)です.実際のラはその周波数を持ったきれいな正弦波ではありません.
ここであらゆる波形は,複数の周波数の正弦波を合成することでできるということを知っているでしょうか.フーリエ解析という数学の技術を使うと,あらゆる波を正弦波に分解することができるのです.これを使って,あるラを分析してみると,複数の高さを持ったラが同時に鳴っていることがわかります.
Processing で複数の周波数の正弦波を合成して出力するには次のようにします.
sine1 = new SineWave(440, 0.5, out.sampleRate()); sine2 = new SineWave(880, 0.2, out.sampleRate()); sine3 = new SineWave(1760, 0.1, out.sampleRate()); out.addSignal(sine1); out.addSignal(sine2); out.addSignal(sine3);
さっきとはちょっと違った音が出たと思います.なお,SineWave メソッドの 2 番目のパラメータは音の強さを指示するもので 0.0 から 1.0 の値を設定することができます.
Processing では正弦波だけでなく,矩形波や三角波(のこぎり派),さらには,自由に形状を指定した波を作って,合成することができます.興味のある人は Minim ライブラリのリファレンスを眺めてみてください.
ここで鳴らしている音波の波形を表示するスケッチをみてみます.PDE のメニューから [File] - [Examples] - [Libraries] - [Minim] - [SineWaveSignal] を選んでください.このスケッチはマウスを上下に動かすと周波数,左右に動かすとスピーカのバランスを変えることができ,また,鳴らしている正弦波の波形を表示します.
次に音源ファイルの波形を見てみます.PDE のメニューから [File] - [Examples] - [Libraries] - [Minim] - [LoadFile] を選んでください.これは AudioPlayer オブジェクトに読み込んだ音源ファイルの波形を表示するスケッチです.
先ほどあらゆる波形は複数の周波数の正弦波から作ることができることを説明しました.どのような周波数が含まれているかを見ることも簡単にできます.PDE メニューから [File] - [Examples] - [Libraries] - [Minim] - [ForwardFFT] を選んでください.これは AudioPlayer オブジェクトに読み込んだ音源ファイルの周波数成分を表示するスケッチです.X 軸が周波数,Y 軸が波の強さを表します.
下の音源ファイルはピアノの音です.これを鳴らすと等間隔で並ぶ線が見えると思います.これは基本周波数の 2 倍,3 倍,...という周波数の正弦波がまざった音であることを示しています.
上記のスケッチは図的には様々な周波数が含まれていることがわかりますが,どのような周波数の音がまざっているかまではわかりません.そこで,マウスで指した部分の周波数がどのような値であるかを表示してみます.
Processing で文字を表示するには,表示する文字のフォントを生成する必要があります.そのためには,PDE のメニューから,[Tool] - [CreateFont] を選びます.すると次のようなダイアログが表示されますので,使いたいフォントの種類とサイズを指定します.
今回は ArialMT の 24 ポイントを使うことにしてみます.また,どのような文字のフォントを作るかを The quick... と表示されているところに入力します.今回は周波数を表示するだけですので,0123456789.kHz と入力すれば十分です.もしくは [All Characters] にチェックを入れてしまっても構いません.なお,日本語フォントで [All Characters] にチェックを入れるとすべての漢字のフォントが生成されるため,作られるファイルのサイズが非常に大きくなるので気をつけてください.また,下に表示される Filename をコードの中で利用しますので,記録しておいてください.
作成したフォントを用いて表示をするには PFont クラスのインスタンスを生成します.そこで,PFont オブジェクトを格納する変数をグローバル変数として宣言し,setup の中でオブジェクトを生成し,格納するコードを入れます.
PFont font; : void setup() { : font = loadFont("ArialMT-24.vlw");
周波数は X 軸で表現していますので,マウスカーソルの位置に表示される周波数の値を得るにはマウスカーソルの X 座標が必要です.これは mouseX で取得できることはもう何度も行ってきたのでわかると思います.
では,X 座標が mouseX の場所に表示される周波数の値はどうなるかというと,
mouseX * fft.getBandWidth()
となります.getBandWidth メソッドは 1 本の縦線に含まれる周波数帯を示しています.つまり,X 座標が x の部分には,getBandWidth()*x 〜 getBandWidth()*(x+1) までの周波数の合計が表示されていることになります.
今回は簡便のために,getBandWidth*mouseX の値を表示することにします.そこで draw メソッドの中に
fill(255); textFont( font ); text( fft.getBandWidth()*mouseX + "Hz", 50, 50 );
を加えてください.ForwardFFT には既に fill と text を使ったコードが入っていますので,それを削除してから加えた方がわかりやすいと思います.
fill は塗りつぶし色を指定するメソッドでした.文字の色は fill メソッドで指定します.textFOnt メソッドはフォントの種類を指定するメソッドで,生成したPFont オブジェクトをパラメータにします.text メソッドは 1 番目パラメータとして渡した文字列を画面上に描くメソッドです.2,3 番目のパラメータで表示する位置を指定します.getBandWidth()*mouseX は数値ですが,文字列リテラルの Hz と加算することで文字列に自動的に変換されます.また,text のパラメータとして数値を与えると自動的に文字列に変換されますので,実は文字列リテラルとの加算をしなくてもきちんと表示されます.
正弦波の波形を表示するで暑かったスケッチをもとにして,出している音の周波数を表示するようにしてみましょう.
なお,周波数はマウスカーソルの Y 座標を用いて,
map(mouseY, 0, height, 1500, 60);
と算出しています.この map メソッドは(第 1 パラメータ,第 2 パラメータ)の範囲を(第 3 パラメータ,第 4 パラメータ)の範囲に変換するメソッドです.この〇の場合は,0 は 1500 に,height は 60 に,(0+height)/2 は (1500+60)/2 に変換をしてくれます.