Skip to content

Commit

Permalink
LPFのみIIRで実装
Browse files Browse the repository at this point in the history
  • Loading branch information
techno-cat committed Oct 30, 2011
1 parent bba81f3 commit 4eeb3b6
Showing 1 changed file with 88 additions and 29 deletions.
117 changes: 88 additions & 29 deletions p5-NeSynth/lib/Sound/NeSynth/Filter.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use 5.008009;
use strict;
use warnings;
use Readonly;
use Math::Trig qw( pi );
use Math::Trig qw( tan pi );
use base qw( Exporter );

our %EXPORT_TAGS = ( 'all' => [ qw(
Expand All @@ -16,19 +16,86 @@ our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our $VERSION = '0.01';

# Qの最小と最大値(このフィルターの仕様)
Readonly my $Q_MIN => 0.1;
Readonly my $Q_MAX => 20.0;
Readonly my $CUTOFF_MIN => 0.0;
Readonly my $CUTOFF_MAX => 0.499; # cutoffが0.5だと計算途中でエラーになるのでこの値でガード

sub create_filter {
my $samples_per_sec = shift;
my $arg_ref = shift;

my $freq = $arg_ref->{freq};
my $q = ( exists $arg_ref->{q} ) ? $arg_ref->{q} : (1 / (2 ** 0.5));
if ( not exists $arg_ref->{type} ) {
die '"type" not found. type: lpf, hpf, bpf, bef'
}

my $type = $arg_ref->{type};
my $cutoff = 0.0;
if ( exists $arg_ref->{cutoff} ) {
$cutoff = $arg_ref->{cutoff};
}
else {
die '"cutoff" not found. cutoff: ' . sprintf( "%f <= cutoff <= %f", $CUTOFF_MIN, $CUTOFF_MAX );
}

if ( $cutoff < $CUTOFF_MIN ) {
die '"cutoff" must be or more ' . $CUTOFF_MIN;
}

if ( $CUTOFF_MAX < $cutoff ) {
die '"cutoff" must be or less ' . $CUTOFF_MAX;
}

my $q = 1.0 / sqrt(2);
if ( exists $arg_ref->{Q} ) {
$q = $arg_ref->{Q};

# hoge
printf( "filter: freq = %.2f, Q = %.2f\n", $freq, $q );
if ( $q < $Q_MIN ) {
die '"Q" must be or more ' . $Q_MIN;
}

if ( $Q_MAX < $q ) {
die '"Q" must be or less ' . $Q_MAX;
}
}

printf( "filter: type: %s, cutoff = %.2f, Q = %.2f\n", $type, $cutoff, $q );

return _create_filter_func( $type, $cutoff, $q );
}

sub _create_filter_func {
my ( $type, $cutoff, $q ) = @_;

if ( $type eq 'lpf' ) {
# todo: LPFの係数算出
}
else {
die 'sorry, coming soon ...';
}

# for LPF
my $fc = tan(pi() * $cutoff) / (2.0 * pi());
my $tmp = 1.0 + ((2.0 * pi() * $fc) / $q) + (4.0 * pi() * pi() * $fc * $fc); # 各係数の分母
my $b0 = (4.0 * pi() * pi() * $fc * $fc) / $tmp;
my $b1 = (8.0 * pi() * pi() * $fc * $fc) / $tmp;
my $b2 = (4.0 * pi() * pi() * $fc * $fc) / $tmp;
my $a1 = ((8.0 * pi() * pi() * $fc * $fc) - 2.0) / $tmp;
my $a2 = (1.0 - ((2.0 * pi() * $fc) / $q) + (4.0 * pi() * pi() * $fc * $fc)) / $tmp;

printf( "%.4f, %.4f, %.4f, %.4f, %.4f\n", $b0, $b1, $b2, $a1, $a2 );

my @buf_a = ( 0.0, 0.0 );
my @buf_b = ( 0.0, 0.0, 0.0 );
return sub {
my $val = shift;
return $val;
unshift @buf_b, $_[0];
pop @buf_b;
my $ret = ($buf_b[2] * $b2) + ($buf_b[1] * $b1) + ($buf_b[0] * $b0)
- ($buf_a[1] * $a2) - ($buf_a[0] * $a1);
unshift @buf_a, $ret;
pop @buf_a;
return $ret;
};
}

Expand All @@ -41,34 +108,26 @@ Sound::NeSynth:Modulator - Modulator module for NeSynth
=head1 SYNOPSIS
use Sound::NeSynth::Modulator;
my $samples_per_sec = 30;
my $type = 'lpf';
my $cutoff = 0.2; # must 0.0 <= cutoff <= 0.499
my $q = 0.7; # must 0.01 <= q <= 20.0
my $filter = create_filter( $samples_per_sec, { type => $type, cutoff => $cutoff, Q => $q } );
my $samples_per_sec = 4;
my $freq = 1;
my $osc = create_osc( $samples_per_sec, $freq );
$osc->(); # 0.0
$osc->(); # 1.0
$osc->(); # 0.0
$osc->(); # -1.0
$osc->(); # 0.0
$osc->(); # 1.0
# -- repeat --
# get response of 0.0 -> 1.0
my @result = map {
printf( "%f\n", $filter->(1.0) );
} 1..10;
=head1 DESCRIPTION
=head2 create_osc
This filter module is for Sound::NeSynth.
it's using IIR filter.
samples per sec = 4
frequency 1(Hz)
=head1 SEE ALSO
Sound::NeSynth
<-- 1sec --><-- 1sec -
+1.0 | o o
|
0.0 o-----o-----o-----> t
|
+1.0 | o
=head1 AUTHOR
techno-cat, E<lt>techno.cat.miau(at)gmail.comE<gt>
Expand Down

0 comments on commit 4eeb3b6

Please sign in to comment.