#!/tools/local/perl -w use strict; use Tk; use Tk::PNG; use GD::Graph::lines; use GD::Graph::bars; use Audio::Data; use Audio::Play; sub PI () { 3.14159265358979323846 } # use Audio::Filter; my $mw = MainWindow->new; my $N = 512; my $rate = 8000; my $svr = Audio::Play->new; use Audio::Filter; my @data; sub nat_glot { my $nopen = 4*30; my $pole2 = Audio::Resonator->new(rate => $rate); my $pole = Audio::Resonator->new(rate => $rate); my $tmpf = $nopen * 0.00833; $pole->setpole(0,$rate/$nopen,$tmpf*$tmpf); $pole2->setpole(0,950*$rate/10000,630*$rate/10000); my @natglot = ( 1200, 1142, 1088, 1038, 991, 948, 907, 869, 833, 799, 768, 738, 710, 683, 658, 634, 612, 590, 570, 551, 533, 515, 499, 483, 468, 454, 440, 427, 415, 403, 391, 380, 370, 360, 350, 341, 332, 323, 315, 307, 300, 292, 285, 278, 272, 265, 259, 253, 247, 242, 237, 231, 226, 221, 217, 212, 208, 204, 199, 195, 192, 188, 184, 180, 177, 174, 170, 167, 164, 161, 158, 155, 153, 150, 147, 145, 142, 140, 137, 135, 133, 131, 128, 126, 124, 122, 120, 119, 117, 115, 113, 111, 110, 108, 106, 105, 103, 102, 100, 99, 97, 96, 95, 93, 92, 91, 90, 88, 87, 86, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 75, 74, 73, 72, 71, 70, 69, 68, 68, 67, 66, 65, 64, 64, 63, 62, 61, 61, 60, 59, 59, 58, 57, 57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 41, 41, 40, 40, 39, 39, 38, 38, 38, 38, 37, 37, 36, 36, 36, 36, 35, 35, 35, 35, 34, 34, 33, 33, 33, 33, 32, 32, 32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 28, 28, 28, 28, 27, 27); my $natb = $natglot[$nopen - 40]; my $nata = ($natb * $nopen) * 0.333; my $decay = 0.33; my $onemd = 1.0 - $decay; my $vwave = 0.0; my $vlast = 0.0; my $en = 13000000; print "nopen=$nopen a=$nata b=$natb\n"; my $nper = 0; for my $s (0..$N-1) { my $voice; for my $i (0..3) { if (1) { if ($nper < $nopen) { $nata -= $natb; $vwave += $nata; $voice = $vwave * 0.028; } else { $vwave = 0.0; $voice = 0.0; } } else { $vwave = ($nper == 1) ? $en : ($nper == 2) ? -$en : 0; $voice = $pole->process($vwave); } $voice = $pole2->process($voice); $nper++; } $voice = $voice * $onemd + $vlast * $decay; $vlast = $voice; push(@data,$voice); } } sub pole_zero { my $nopen = 4*30; my $tmpf = $nopen * 0.00833; my $pole = Audio::Resonator->new(rate => $rate); my $pole2 = Audio::Resonator->new(rate => $rate); my $zero = Audio::AntiResonator->new(rate => $rate); $pole->setpole(0,$rate/$nopen,$tmpf*$tmpf); $pole2->setpole(0,950*$rate/10000,630*$rate/10000); #$zero->setzero(1875,150); push(@data,$pole2->process($pole->process(0.0))); push(@data,$pole2->process($pole->process(1.0))); push(@data,$pole2->process($pole->process(-1.0))); for my $i (3..$N-1) { push(@data,$pole2->process($pole->process(0.0))); } # print join(',',$pole->data),"\n"; } sub fir { my $fir = Audio::Filter::FIR->new(rate => $rate); my @h; $h[0] = $h[19] = -0.03257; $h[1] = $h[18] = 0.04384; $h[2] = $h[17] = 0.02071; $h[3] = $h[16] = -0.05702; $h[4] = $h[15] = -0.00516; $h[5] = $h[14] = 0.07675; $h[6] = $h[13] = -0.02234; $h[7] = $h[12] = -0.12071; $h[8] = $h[11] = 0.11191; $h[9] = $h[10] = 0.48459; push(@h,(0) x 19); $fir->data(@h); push(@data,$fir->process(1.0)); for my $i (1..$N-1) { push(@data,$fir->process(0)); } } sub impulse { return (1,(0) x ($N-1)); } my %formants = ( N => [[480,40,35],[1780,300,35],[2620,360,35]], OR => [[490,60,50.75],[820,90,45.5],[2500,150,22,75]], EE => [[250,60,50.75],[2350,90,33.25],[3200,150,36.75]], ); sub dB { my $dB = shift; return 32767*10**(($dB-87)/20-3); } sub formants { my $f = $formants{'EE'}; my @impulse = impulse(); if (1) { my $f1 = Audio::Resonator->new(rate => $rate)->setpole(@{$f->[0]}[0,1]); my $f2 = Audio::Resonator->new(rate => $rate)->setpole(@{$f->[1]}[0,1]); my $f3 = Audio::Resonator->new(rate => $rate)->setpole(@{$f->[2]}[0,1]); push(@data,$f3->process($f2->process($f1->process(@impulse)))); } else { my $f1 = Audio::Resonator->new(rate => $rate)->setpole(@{$f->[0]}[0,1],dB($f->[0][2])); my $f2 = Audio::Resonator->new(rate => $rate)->setpole(@{$f->[1]}[0,1],dB($f->[1][2])); my $f3 = Audio::Resonator->new(rate => $rate)->setpole(@{$f->[2]}[0,1],dB($f->[2][2])); foreach my $s (@impulse) { push(@data,$f3->process($s)+$f2->process($s)+ $f1->process($s)); } } } my $last = 0.0; sub lowpass { my $val = shift; $val = $val + 0.75 * $last; $last = $val; return $val; } sub hipass { my $val = shift; my $out = $val - $last; $last = $val; return $out; } #pole_zero(); nat_glot(); sub hilo { foreach my $val (sqrt($rate),(0) x ($N-1)) { push(@data,hipass($val)); } my $energy = 0.0; foreach my $v (@data) { $energy += ($v*$v)/$rate; } printf "energy %g power=%g\n",$energy,$energy/($N*$rate); } #formants(); my $sound = Audio::Data->new(rate => $rate, silence => 0.5); my @sound; my $f0 = 150; my $samp = int($rate/$f0); for (my $i = 0; $i < $rate/2; $i += $samp) { splice(@sound,$i,80,@data[0..$samp-1]); } $sound->data(@sound); sub play { $svr->play($sound); $svr->flush; } #fir(); my $au = new Audio::Data rate => $rate; $au->data(@data); my $org = Audio_image($mw,$au, Method => 'amplitude', Xfunc => \&datax, x_label => 'Time', x_label_skip => 5*$N/64, # x_tick_number => 'auto', y_label => 'Amp', title => 'Original'); $au->r2_fft; my $ffta = Audio_image($mw,$au, Class => 'bars', # Method => 'dB', Method => 'amplitude', Xfunc => \&fftx, Range => [0,$N/2], x_label => 'Freq', x_label_skip => 5*$N/64, x_ticks => 0, y_number_format => sub { sprintf("%6.1f",$_[0]) }, y_label => 'Amp', title => 'FFT'); my $fftp = Audio_image($mw,$au, Class => 'bars', Method => 'phase', Xfunc => \&fftx, Range => [0,$N/2], x_label => 'Freq', x_label_skip => 5*$N/64, y_label => 'Phase', y_number_format => sub { sprintf("%6.1f",$_[0]*180/PI) }, y_min_value => -PI(), y_max_value => PI, y_tick_number => 8, title => 'FFT'); $mw->Button(-text => 'Play', -command => \&play)->pack; $mw->Label(-image => $org,-anchor => 'e')->pack(-anchor => 'e',-fill => 'x'); $mw->Label(-image => $ffta,-anchor => 'e')->pack(-anchor => 'e',-fill => 'x'); $mw->Label(-image => $fftp,-anchor => 'e')->pack(-anchor => 'e',-fill => 'x'); MainLoop; sub xfftx { my ($x,$au,$v) = @_; my $n = $au->samples; my $f = $au->rate; unless ($au->amplitude($x) > 1e-3) { # undef $_[2]; return ''; } return $x * $f / $N; } sub fftx { my ($x,$au,$v) = @_; my $n = $au->samples; my $f = $au->rate; return $x * $f / $N; } sub datax { my ($x,$au) = @_; my $n = $au->samples; my $f = $au->rate; return 1000 * $x / $f; } sub Audio_image { my ($mw,$au,%args) = @_; my $meth = delete $args{Method}; my $xfunc = delete $args{Xfunc}; my $range = delete $args{Range} || [0,$au->samples-1]; my $class = delete($args{Class}) || 'lines'; my $graph = "GD::Graph::$class"->new(640,240); $meth ||= 'data'; $graph->set(%args); my @data = $au->$meth(@$range); my $n = @data; my @x = (0..$n-1); if (defined $xfunc) { foreach my $x (@x) { $x = &$xfunc($x,$au,$data[$x]); } } my $gd = $graph->plot([\@x,\@data]); my $file = "temp$$.png"; open(IMG, ">$file") or die "Cannot open $file:$!"; binmode IMG; print IMG $gd->png; close IMG; my $img = $mw->Photo(-format => 'png', -file => $file); unlink($file); return $img; }