Audio::PortAudio(実践編1)
前回との差分は、エンベロープが追加された辺りなんだけど、
音程を変えるタイミングでtrigger
を呼ぶと鍵盤を押した感じになる。
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 $eg = Cassis::EG->new( fs => SAMPLING_RATE, adsr => [ 0.02, 0.02, 0.5, 1.0 ], curve => 0.7 ); my $amp = Cassis::Amp->new(); my $interval = SAMPLING_RATE; my $i = $interval; # Infinite loop... while ( 1 ) { my $wa = $stream->write_available; if ( $interval <= $i ) { $eg->trigger( gatetime => 0.3 ); my $pitch = shift @pitch_table; $dco->set_pitch( 4.0 + $pitch ); push @pitch_table, $pitch; $i -= $interval; } my $amp_out = $amp->exec( src => $dco->exec( num => $wa ), mod_volume => { src => $eg->exec( num => $wa ), depth => 1.0 } ); my $buf = pack( 's*', map { int( (($_ < -1.0) ? -1 : ((1.0 < $_) ? +1 : $_)) * INT16_MAX ); } @{$amp_out} ); $stream->write( $buf ); $i += $wa; } }
最初は、note_on
とnote_off
っていうサブルーチンを用意してたんだけど、
note_off
を呼ぶの面倒すぎると判断したので、
trigger
の引数にgatetime
を指定するような実装にした。
あと1回くらい、似たようなコードを披露することになると思うけど、
たぶん、予想通りかと思います。
おしまい。
Leave a Comment