Perl Hackers Hub

第15回Perl meets beats―鳴らして学ぶシンセサイザー入門(1)

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回は@techno_nekoこと伊藤智章さんが、Perlで音を扱う方法について紹介します。

Perlで「音」は扱える?

Perlと言えば文字列操作やWebアプリケーションのイメージが強いと思いますが、今回は「音」を扱って信号処理を行うという、ちょっと特殊なPerlの使い方を紹介します。まずPerlで音を作って、次にビートを刻み、最後にWAVEファイルに出力する方法を説明します。

なお、本稿のコードはWEB+DB PRESS Vol.69のサポートサイトからダウンロードできますので、ぜひみなさんも実際に動かして音を聴いてみてください。

波形のお話

普段みなさんが耳にしている音は、とても複雑な波形(空気の振動)です。今回はその複雑な波形を作るために、いくつかの基本波形を合成して音を作ります図1⁠。これらの波形は、三角関数のように同じデータを繰り返すものと、乱数を用いて生成したノイズで表現されます。

図1 波形の種類
図1 波形の種類

リスト1は波形を生成する関数を返すコードです。最初に、ノイズ波形のもととなるノイズデータの初期化を行います。ノイズ波形とは、rand()の戻り値のように周期を持たない波形を指します。ノイズデータは、好みの音が出る状態を保持するために、(1)のようにsrand()による初期化を行ってあらかじめ配列に格納しておきます。これにより、今回の音作りに適したノイズ波形を安定的に生成できます。実際に波形データを生成する関数は(2)のように定義して、あとのコードでこれらを簡単に呼び出せるように、波形の名前を指定して関数を取得できる(3)のcreate_mod_func()を定義しています。

リスト1 波形を生成する関数
use strict;
use warnings;
use Math::Trig qw( pi );

srand( 2 ); # (1)好みの音が得られるように初期化
my @noise = map { rand( 2.0 ) - 1.0; } 1..1024;

my %func_table = ( # (2)
    'pulse' => sub { # 矩形波
        return ( $_[0] < 0.5 ) ? -1.0 : 1.0;
    },
    'sin' => sub { # サイン波(正弦波)
        return sin( 2.0 * pi() * $_[0] );
    },
    'saw' => sub { # のこぎり波
        return ( 2.0 * $_[0] ) - 1.0;
    },
    'tri' => sub { # 三角波
        if ( $_[0] < 0.5 ) {
            # -1.0 -> +1.0
            return -1.0 + ( 4.0 * $_[0] );
        }
        else {
            # +1.0 -> -1.0
            return 1.0 - ( 4.0 * ($_[0] - 0.5) );
        }
    },
    'noise' => sub { # ノイズ
        if ( $_[0] < 1.0 ) {
            my $idx = int( $_[0] * scalar(@noise) );
            return $noise[$idx];
        }
        else {
            return 0.0;
        }
    }
);

sub create_mod_func { # (3)
    my $func = $func_table{$_[0]} or die;
    return $func;
}

音程のお話

図2のように、1オクターブを12等分して算出した音律(周波数と音程の関係)を十二平均律と言います。今回は、一般的に多く採用されている基準となるラの音を440Hzとした場合の、残りの音程の周波数を算出する方法を紹介します。

図2 音程と周波数
図2 音程と周波数

Perlによる実装

リスト2は、図2のindexと周波数の関係をコードに落とし込んだものです。たとえばドの音の周波数を計算したい場合は、図2の横軸からドに対応するindexを選択して、(1)のように関数の引数として与えて算出します。さらに1オクターブ高い音の周波数を計算する場合は(2)のように12足したものを与え、逆に1オクターブ低い音の周波数を計算する場合は(3)のように12引いたものを引数に与えます。

リスト2 indexから周波数を求める
sub index_to_freq {
    my $index = shift;
    return 440.0 * ( 2.0 ** ($index / 12.0) );
}

# ドの音の周波数を計算する
my $freq_C2 = index_to_freq( 3 );      # (1)
my $freq_C3 = index_to_freq( 3 + 12 ); # (2)
my $freq_C4 = index_to_freq( 3 - 12 ); # (3)

おすすめ記事

記事・ニュース一覧