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