Perl Hackers Hub

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

この記事を読むのに必要な時間:およそ 4.5 分

音の作り方[入門編]

(1)で紹介した関数を使って,任意の周波数を持った波形データを生成する方法を説明します。これによってビープ音を鳴らすことができます。

波形の生成

任意の周波数を持った波形データを生成するには,サンプリング周波数について知る必要があります。図3のように,もともと滑らかな波形を一定間隔で記録することをサンプリングと言います。サンプリング周波数とは1秒間に行うサンプリングの回数を意味していて,1秒間をこの回数で割った間隔①でサンプリングして波形データを生成します。任意の周波数を持った波形データを生成するには,1秒間をその任意の周波数で分割した②の範囲で繰り返す波形をサンプリングします。

図3 波形データの例

図3 波形データの例

リスト3は,任意の周波数を持った波形データを生成するコードです。(1)の関数は,引数で与えられた条件で波形データを生成する関数を返します。(2)で1周期あたりのデータ数を算出して,(3)で波形のサンプリング位置を0.0以上1.0未満に正規化した値を引数で渡しています。1秒間の波形データを生成する場合は,(4)で取得した関数をサンプリング周波数と同じ数だけ呼び出します。(5)の引数については次の周波数変調で説明します。

リスト3 波形の生成

sub create_modulator { # (1)
    my $samples_per_sec = shift;
    my $arg_ref = shift;

    my $freq = $arg_ref->{freq};
    my $osc_func = create_mod_func( $arg_ref->{waveform} );
    my $t = 0.0;
    my $samples_per_cycle = $samples_per_sec / $freq; # (2)
    return sub {
        my $mod = shift;
        my $ret = $osc_func->(
            $t / $samples_per_cycle # (3)
        );

        my $dt = 1.0 + $mod;
        if ( 0.0 < $dt ) {
            $t += $dt;
            while ( $samples_per_cycle <= $t ) {
                $t -= $samples_per_cycle;
            }
        }

        return $ret;
    };
}

# 1秒間分の440Hzのサイン波を生成する
my $osc = create_modulator(  # (4)
    44100,                   # サンプリング周波数
    {
        freq => 440,         # 周波数
        waveform => 'sin'    # 波形の種類
    }
);
my @sin_samples = map {
    $osc->( 0 );             # (5)
} 1..44100;

周波数変調

ここでは,時間の経過と共に音の周波数を変化させるモジュレーション(周波数変調)について説明します図4)⁠

図4 周波数変調の例

図4 周波数変調の例

図4は,①の矩形波によって,②の周波数が一定なサイン波を周波数変調しています。周波数変調を行うと③の波形のように,①の出力がマイナスの区間aは周波数が低くなり,①の出力がプラスの区間bは周波数が高くなります。

Perlによる実装

リスト4は周波数変調を実装したコードです。(1)の関数を使って周波数変調用の関数を取得し,(2)でその出力である図4①のような波形を引数に入力しています。リスト3(5)では引数に0を与えているため図4②のように周波数が一定の波形が生成され,リスト4では図4③のように入力された値で周波数が変調された波形が生成されます。create_envelope()で生成される波形については,次の減衰音で説明します。

リスト4 変調用の波形の生成

sub create_pitch_modulator { # (1)
    my $samples_per_sec = shift;
    my $arg_ref = shift;

    my $waveform = $arg_ref->{waveform};
    my $depth = $arg_ref->{depth};
    if ( $waveform eq 'env' ) {
        my $curve = 1.0;
        if ( exists $arg_ref->{curve} ) {
            $curve = $arg_ref->{curve};
        }
        my $env = create_envelope(
            $samples_per_sec,
            { sec => $arg_ref->{speed}, curve => $curve } );
        return sub { $env->() * $depth };
    }
    else {
        my $osc = create_modulator(
            $samples_per_sec,
            {
                freq => (1.0 / $arg_ref->{speed}),
                waveform => $waveform
            }
        );
        return sub { $osc->(0) * $depth };
    }
}

# 周波数変調用の関数の取得
my $mod = create_pitch_modulator(
    44100,                  # サンプリング周波数
    {
        speed => 0.5,       # 変調する速度
        depth => 0.5,       # 変調の度合い
        waveform => 'pulse' # 矩形波
    }
);

# リスト3で定義した波形を周波数変調する
my @sin_with_mod_samples = map {
    $osc->( $mod->() ); # (2)
} 1..44100;

減衰音

太鼓や鐘の音のように,時間の経過と共に音量が小さくなる音を減衰音と言います図5)⁠図5①のように音量(振幅)が一定の波形に対して,②のように+1.0から0.0に変化する波形を掛け合わせると,③のように音量が変化する波形データを生成できます。これらはシンセサイザーでは「エンベロープ」という名称で搭載されていますが,その簡易版をPerlで実装します。

図5 減衰音の例

図5 減衰音の例

Perlによる実装

リスト5は,図5②のような波形を生成する関数を返します。+1.0から0.0に変化させる際に,一定の割合で変化させるか,図5のように最初は急に,後半は緩やかに変化させるかで音のニュアンスが変わってきます。このニュアンスを出す処理はリスト5(1)で行っています。

リスト5 減衰音の例

sub create_envelope {
    my $samples_per_sec = shift;
    my $arg_ref = shift;

    my $curve = 1.0;
    if ( exists $arg_ref->{curve} ) {
        $curve = $arg_ref->{curve};
    }
    my $mod_func = sub { return ( 1.0 - $_[0] ); };
    my $t = 0.0;
    my $interval = $samples_per_sec * $arg_ref->{sec};
    return sub {
        if ( $t < $interval ) {
            my $ret = $mod_func->( $t / $interval );
            $t += 1.0;
            return $ret ** $curve; # (1)
        }
        else {
            return 0.0;
        }
    };
}

著者プロフィール

伊藤智章(いとうともあき)

組み込み系の会社に所属し,ファームウェアとGUIアプリケーションの開発を担当。その開発作業の効率化のためにPerlを使用している。

主に「Hokkaido.pm」に出没して,あご髭が長いというだけで北海道のPerl(?)仙人と呼ばれている。

Twitter:techno_neko

写真:Japan Perl Association

コメント

コメントの記入