Arduino用の正弦波を検討してみる
+256から-256の範囲で正弦波を出力したいんだけど、
理想的には配列も添字も8bit変数に納めたくて、いろいろやってみた。
use v5.14; use strict; use warnings; use Imager; use Math::Trig qw( pi ); use constant PI => pi(); my $img = Imager->new( xsize => 1000, ysize => 600 + 1 ); $img->box( filled => 1, color => 'white' ); draw_wave( $img, 'green' , create_sin_osc() , .00, .0015 ); draw_wave( $img, 'blue' , create_sin_osc1(), .10, .0015 ); draw_wave( $img, 'purple', create_sin_osc2(), .20, .0015 ); draw_wave( $img, 'red' , create_sin_osc3(), .30, .0015 ); $img->write( file => "graph.png" ) or die $img->errstr; sub draw_wave { my ( $img, $color, $osc, $t0, $dt ) = @_; my $y0 = int($img->getheight() / 2); my $ymax = $y0; my $xmax = $img->getwidth() - 1; foreach my $x ( 0..$xmax ) { my $t = $t0 + ($x * $dt); $img->setpixel( x => $x, y => ($y0 - $osc->($t)), color => $color ); } # my @points = map { # [ $_, ($y0 - $osc->($_ * 0.001)) ]; # } 0..$xmax; # $img->polyline( points => \@points, color => $color ); } sub create_sin_osc { return sub { my $t = shift; return int( 256.0 * sin($t * 2.0 * PI) ); }; } sub create_sin_osc1 { my $n = 256; my @wav = map { my $tmp = sin( ($_ / $n) * (2.0 * PI) ); int( $tmp * 64.0 ); } 0..($n - 1); return sub { my $t = shift; return $wav[ int($t * $n) % $n ] * 4; }; } sub create_sin_osc2 { my $n = 256; my $mask = $n - 1; my @wav = map { my $tmp = sin( ($_ / $n) * (2.0 * PI) ); if ( $tmp < .0 ) { int( ($tmp * (128.0 / 1.125)) - .5 ); } else { int( ($tmp * (128.0 / 1.125)) + .5 ); } } 0..($n - 1); return sub { my $t = shift; my $i = int( $t * $n ); return int( $wav[$i & $mask] * 1.125 * 2 ); }; } sub create_sin_osc3 { my $n = 128; my $mask = $n - 1; my @wav = map { my $tmp = sin( ($_ / $n) * (2.0 * PI) ); if ( $tmp < .0 ) { int( ($tmp * (128.0 / 1.125)) - .5 ); } else { int( ($tmp * (128.0 / 1.125)) + .5 ); } } 0..($n - 1); say $_ for @wav; return sub { my $t = shift; my $i = int( $t * $n * 4 ); my $rest = $i & 0x03; $i >>= 2; my $val = $wav[$i & $mask]; my $diff = $wav[($i + 1) & $mask] - $val; my $ret = (($val * 4) + ($diff * $rest)) * 1.125; return int( $ret / 2 ); }; }
LFOに使いたくて補間までやってみたけど、
補間するなら要素数を減らして、16bitの配列を使うのが良さそう。
なので、今回は補間なしで実装しようと思う。
もう一つの理由は、
矩形波やのこぎり派でも補間されちゃうのは困るので、
音を出す用とLFOで処理を分けようと思う。
おしまい。
Leave a Comment