本日のボツスクリプト
YAPC::ASIA Tokyo 2011で「Perl meets beats.」を話したときに、
「フィルターはあるのかな?」みたいなツイートを見かけて以来、
ずっとやらなきゃなーって思ってて、結果的に作り直してるんだけど、
そのうち、フィルターの部分はできたと思う。
リポジトリはこちら。
https://github.com/techno-cat/p5-Cassis
んでもって、ボツスクリプト。
use v5.14; use strict; use warnings; use Cassis; use Math::Trig qw(pi tan); use List::Util qw(max min); use constant N => 1000; use Imager; # see also "Imager::Font" my $font_size = 12; my $font = Imager::Font->new( file => '/Library/Fonts/Osaka.ttf' ) or die; my $cutoff = 0.2; my $q = 2.5; my @graph_params = ( { type => 'LPF', params => Cassis::Iir2::LPF->new( cutoff => $cutoff, q => $q )->params() }, { type => 'HPF', params => Cassis::Iir2::HPF->new( cutoff => $cutoff, q => $q )->params() }, { type => 'BPF', params => Cassis::Iir2::BPF->new( cutoff => $cutoff, q => $q )->params() }, { type => 'BEF', params => Cassis::Iir2::BEF->new( cutoff => $cutoff, q => $q )->params() } ); foreach ( @graph_params ) { my $type = $_->{type}; my $params = $_->{params}; my $title = sprintf( "%s ( Cutoff: %.3f, Q: %.3f )", $type, $cutoff, $q ); print "=== $title ===\n"; foreach my $key ( qw/b0 b1 b2 a1 a2/ ) { printf( "%s: %6.3f\n", $key, $params->{$key} ); } my @graph_src = map { my $f = $_ / N; my $gain = calc_gain( $f, $params ); { f => $f, gain => $gain }; } 0..(N / 2); my $img = draw_amplitude_spectrum( \@graph_src, $title ); $img->write( file => "amplitude_spectrum_$type.png" ) or die $img->errstr; } sub draw_amplitude_spectrum { my ( $graph_src, $title ) = @_; my %margin = ( left => 30, top => 20, right => 20, bottom => 20 ); my $tick_gain = 100; my $graph_width = scalar(@{$graph_src}) + 1; my $graph_height = 400 + 1; my $width = $margin{left} + $graph_width + 1 + $margin{right}; my $height = $margin{top} + $graph_height + 1 + $margin{bottom}; my $img = Imager->new( xsize => $width, ysize => $height ); $img->box( filled => 1, color => 'black' ); $img->align_string( x => $width / 2, y => 2, halign => 'center', valign => 'top', string => $title, font => $font, color => 'white', size => $font_size ); my $x0 = $margin{left}; my $y0 = $margin{top} + $graph_height - 1; my $x = 0; my $x_step = ($graph_width - 1) / 10; while ( $x < $graph_width ) { $img->line( color => 'white', x1 => $x0 + $x, y1 => $y0, x2 => $x0 + $x, y2 => $y0 - ($graph_height - 1) ); $img->align_string( x => $x0 + $x, y => $y0 + 5, halign => 'center', valign => 'top', string => sprintf( "%.2f", ($x / ($graph_width - 1)) * 0.5 ), font => $font, color => 'white', size => $font_size ); $x += $x_step; } my $y = 0; while ( $y < $graph_height ) { $img->line( color => 'white', x1 => $x0, y1 => $y0 - $y, x2 => $x0 + ($graph_width - 1), y2 => $y0 - $y ); if ( 0 < $y ) { $img->align_string( x => $x0 - 5, y => $y0 - $y + int($font_size * 0.4), halign => 'right', valign => 'baseline', string => sprintf( "%.1f", $y / $tick_gain ), font => $font, color => 'white', size => $font_size ); } $y += $tick_gain / 2; } $x = 0; foreach ( @{$graph_src} ) { my $f = $_->{f}; my $gain = $_->{gain}; $img->setpixel( color => 'red', x => $x0 + $x, y => $y0 - int(($gain * $tick_gain) + 0.5) ); $x++; } return $img; } sub calc_gain { my ( $w, $params ) = @_; # H(z) = (b0 + b1 * z^-1 + b2 * z^-2) / (1 + a1 * z^-1 + a2 * z^-2) # z^1 = e^(jw) = cos(w) + j*sin(w) # z^-1 = e^(-jw) = cos(w) - j*sin(w) # H(jw) = {(A-jB) * (C+jD)} / {(C-jD) * (C+jD)} = {(AC + BD) + j(AD - BC)} / (C^2 + D^2) # |H(jw)| = sqrt( (AC + BD)^2 + (AD - BC)^2 ) / (C^2 + D^2) my ( $b0, $b1, $b2, $a1, $a2 ) = map { $params->{$_}; } qw(b0 b1 b2 a1 a2); my $sin_w = sin( 2.0 * pi * $w ); my $sin_2w = sin( 2.0 * pi * 2.0 * $w ); my $cos_w = cos( 2.0 * pi * $w ); my $cos_2w = cos( 2.0 * pi * 2.0 * $w ); my $largeA = $b0 + ($b1 * $cos_w) + ($b2 * $cos_2w); my $largeB = ($b1 * $sin_w) + ($b2 * $sin_2w); my $largeC = 1.0 + ($a1 * $cos_w) + ($a2 * $cos_2w); my $largeD = ($a1 * $sin_w) + ($a2 * $sin_2w); my $AC_plus_BD = ($largeA * $largeC) + ($largeB * $largeD); my $AD_minus_BC = ($largeA * $largeD) - ($largeB * $largeC); my $d = ($largeC * $largeC) + ($largeD * $largeD); my $gain = sqrt(($AC_plus_BD * $AC_plus_BD) + ($AD_minus_BC * $AD_minus_BC)) / $d; return $gain; }
OSによって、フォントの指定方法が違うみたいのがあって、
これをサンプルとして入れるのは諦めた。
その代わり、振幅特性を数字で出力するスクリプトを同梱することにした。
という訳で、Win32の人はImager::Fontを参考に、
$font
の初期化方法を書き直して貰うとして、
Macの人はこちらの記事を見て貰うとして、
あとは分かんないです、ごめんなさい。
2014/10/28 追記
今日時点でのCassisで動作するようにコードを変更。
おしまい。
Leave a Comment