Audio::PortAudio(入門編)

あらかじめ断っておくと、
MacOS Xを使っていて、homebrewportaudioをインストール出来て、
cpanmAudio::PortAudioをインストールできることを前提に書いています。

use strict;
use warnings;
use Cassis;
use Audio::PortAudio;

use constant SAMPLING_RATE => 44100;
use constant INT16_MAX => 0x7FFF;

my @pitch_table = (
    -9 / 12, # C
    -7 / 12, # D
    -5 / 12, # E
    -4 / 12, # F
    -2 / 12, # G
     0 / 12, # A
     2 / 12, # B
    (-9 / 12) + 1.0 # C(ここから1オクターブ上がる)
);

play();

sub play {
    my ( $frames_per_buffer, $stream_flags ) = ( 512, undef );
    my $api = Audio::PortAudio::default_host_api();
    printf STDERR "Going to play via %s\nCtrl+c to stop...", $api->name;
    my $device = $api->default_output_device;
    my $stream = $device->open_write_stream( {
            channel_count => 1, # 1:mono, 2:stereo
            sample_format => 'int16' #  'float32', 'int16', 'int32', 'int24', 'int8', 'uint8'
        },
        SAMPLING_RATE,
        $frames_per_buffer,
        $stream_flags,
    );

    my $dco = Cassis::DCO::Tri->new( fs => SAMPLING_RATE );
    my $interval = int( SAMPLING_RATE * 0.5 );
    my $i = $interval;

    # Infinite loop...
    while ( 1 ) {
        my $wa = $stream->write_available;

        if ( $interval <= $i ) {
            my $pitch = shift @pitch_table;
            $dco->set_pitch( 4.0 + $pitch );
            push @pitch_table, $pitch;
            $i -= $interval;
        }

        my $dco_out = $dco->exec( num => $wa );

        my $buf = pack( 's*', map {
            int( (($_ < -1.0) ? -1 : ((1.0 < $_) ? +1 : $_)) * INT16_MAX );
        } @{$dco_out} );

        $stream->write( $buf );
        $i += $wa;
    }
}

コードの説明をする前に、PortAudioを使用する準備をします。
cpanmでテストが走った際にも音が出るので、
インストール前にスピーカーの音量は小さめにしておくと良いです。

$ brew install portaudio
$ cpanm Audio::PortAudio

そしてコードの方ですが、あんまり単純だと面白くないので、
約0.5秒ごとに音程が変わるようにしました。

ポイントとしては、
Cassisのモジュールはサンプル数を指定して波形を生成できるので、
Port::Audioとの相性が良いという事です。

でも、効果音を作る時は時間を指定したいですよね。。。
なので、issueを登録した次第。

おしまい。

Leave a Comment