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