8 use List
::Util
qw(min max sum);
13 our ($dsp, $dspin, $ana);
15 our ($chan, $fmt, $rate, $sps) = (1, 8, 32768, 16);
16 our $bps = ($chan * $fmt * $rate) / 8;
17 our $buf = $bps / $sps;
19 our @avgsteps = (4, 8);
21 # How much to amplify the spectrum
24 # Rectangles cover averages in various bands. They also include averages
25 # of surrounding of the rectangle picture. E.g. with step 20, over 10,
26 # the average includes average of 20 of the covered area + 10 on each side.
30 our $draw_lines = 0; # this makes for real pretty graphs but it is a huge slowdown
35 my ($finfo, $label, $reflabel, $fir, $srows, $xofs, $yofs, $w, $h, $hs) = @_;
37 my @f = @
{$finfo->{"freq_$label"}};
38 my @fref; defined $reflabel and @fref = @
{$finfo->{"freq_$reflabel"}};
39 my @r = @
{$finfo->{"rect_$label"}};
40 my @rref; defined $reflabel and @rref = @
{$finfo->{"rect_$reflabel"}};
41 for my $y (0..($srows-1)) {
42 for my $x (0..($w-1)) {
43 my $hb = $yofs + $h * ($y + 1) + $hs * $y;
47 my $barh = $h * ($draw_rect ?
2/3 : 1);
48 my $bar = $h * $f[$i] * $amplifier;
49 $bar = min
($barh, $bar);
50 $canvas->createLine($xofs + $x, $hb, $xofs + $x, $hb - $bar);
51 if (defined $reflabel) {
52 my $refbar = $h * $fref[$i] * $amplifier;
53 $refbar = min
($barh, $refbar);
55 $canvas->createLine($xofs + $x, $hb - $bar, $xofs + $x, $hb - $refbar, -fill
=> 'darkred');
57 $canvas->createLine($xofs + $x, $hb - $refbar, $xofs + $x, $hb - $bar, -fill
=> 'red');
62 if ($draw_rect and !($i % $rect_step) and $i >= $rect_over and $i <= @f - $rect_step - $rect_over) {
63 my $recth = $h * ($draw_lines ?
2/3 : 1);
64 my $avg = $recth * $amplifier * $r[$i / $rect_step];
65 $avg = min
($h, $avg);
67 $canvas->createRectangle($xofs + $x, $rhb, $xofs + $x + $rect_step, $rhb + $avg, -fill
=> 'black');
68 if (defined $reflabel) {
69 my $refavg = $recth * $amplifier * $rref[$i / $rect_step];
70 $refavg = min
($h, $refavg);
72 $canvas->createRectangle($xofs + $x, $rhb + $avg, $xofs + $x + $rect_step, $rhb + $refavg, -fill
=> 'darkred', -outline
=> 'darkred');
74 $canvas->createRectangle($xofs + $x, $rhb + $refavg, $xofs + $x + $rect_step, $rhb + $avg, -fill
=> 'red', -outline
=> 'red');
80 $canvas->createLine($xofs + $x, $hb + 0, $xofs + $x, $hb + 5, -fill
=> 'blue');
81 $canvas->createText($xofs + $x, $hb + 15, -fill
=> 'blue', -font
=> 'small', -text
=> $fi[$i]);
85 $canvas->createText($xofs, $yofs, -fill
=> 'darkgreen', -font
=> 'small', -text
=> $label);
90 my @fi = @
{$finfo->{freqi
}};
93 my $srows = 32/$sps; # spectrum rows; "density" of data depends on sps
94 my $w = @fi / $srows; # width
95 my $ws = 20; # spacing
97 my $hs = 20; # spacing
99 my $htot = $hs + ($h + $hs) * $srows;
103 my $arows = @avgsteps / $acols; # rows of plots; each plot is further divided to $srows rows of spectrum
105 my $ahtot = ($htot - 3 * $hs) / $arows;
106 my $ah = $ahtot / $srows - $hs;
109 $canvas = $mw->Canvas(-width
=> $ws + $w + $ws*3 + ($aw + $ws) * $acols + $ws, -height
=> $htot);
112 $canvas->delete('all');
114 render_spectrum
($finfo, 'now', $avgsteps[0], $finfo->{freqi
}, $srows, $ws, $hs, $w, $h, $hs);
115 for my $i (0..$#avgsteps) {
116 my $ax = $i % $acols;
117 my $ay = sprintf('%d', $i / $acols);
118 my $su = $avgsteps[$i];
119 render_spectrum
($finfo, $su, $i < $#avgsteps ?
$avgsteps[$i+1] : undef, $finfo->{freqi
}, $srows,
120 $ws + $w + $ws*3 + ($ws + $aw) * $ax,
121 ($ahtot + $hs*2) * $ay + $hs,
128 # Array with frequencies corresponding to elements of fftsig
131 my $dft_size = $rate / $sps;
132 for (my $i = 0; $i < $dft_size / 2; $i++) {
133 $freqs[$i] = $i / $dft_size * $rate;
142 $fmt == 8 or die "bits per sample must be 8";
143 while (length($bytes) > 0) {
144 my $sample = unpack('c', substr($bytes, 0, 1, ''));
145 push(@samples, $sample);
148 # Magic from Audio::Analyze:
149 my $fft = Math
::FFT
->new(\
@samples);
150 my $coeff = $fft->rdft;
151 my $size = scalar(@
$coeff);
155 $mag[$k] = sqrt($coeff->[$k*2]**2);
156 for($k = 1; $k < $size / 2; $k++) {
157 $mag[$k] = sqrt(($coeff->[$k * 2] ** 2) + ($coeff->[$k * 2 + 1] ** 2));
161 my $avgmag = sum
(@mag) / @mag; #[1000..$#mag];
162 @mag = map { $_ / $avgmag * 0.3 } @mag;
167 my ($finfo, $l) = @_;
168 my @f = @
{$finfo->{"freq_$l"}};
170 for (my ($fi, $ri) = ($rect_step, 1); $fi <= $#f - $rect_step - $rect_over; $fi += $rect_step, $ri++) {
171 $r[$ri] = sum
(@f[$fi - $rect_over .. $fi + $rect_step + $rect_over]) / ($rect_step + 2 * $rect_over);
173 $finfo->{"rect_$l"} = \
@r;
181 my $w = $dsp->dread();
182 $finfo->{freqi
} = [fftfreq
()];
183 $finfo->{freq_now
} = [fftsig
($w)];
185 # Create averaged rects
186 rectsof
($finfo, 'now');
188 # Update floating freq. averages
189 for my $su (@avgsteps) {
190 for my $i (0..$#{$finfo->{freqi}}) {
191 my $fn = $finfo->{freq_now
}->[$i];
192 my $fs = $finfo->{"freq_$su"}->[$i];
194 $finfo->{"freq_$su"}->[$i] = ($fs * ($su - 1) + $fn) / $su;
196 rectsof
($finfo, $su);
208 $dsp = new Audio
::DSP
(buffer
=> $buf,
212 $dsp->init() || die $dsp->errstr();
215 $mw = MainWindow
->new;
217 $mw->after(1, \
&ticks
);