http://www.perlmonks.org?node_id=478426
Category: GUI Programming
Author/Contact Info true
Description: I had to turn off strict for the local $\ to work correctly, but this perl program will visually plot an 8bit wav and a 16bit wav mono or stereo. It also lets you listen to the wav and updates a progress bar as it is playing. You can also resize the GUI window which resizes the plot graph also. I added this snippet as thanks for the help i got in this thread.
#!C:/Perl/bin/perl
#use strict;
use Win32;
use Win32::Sound;
use Win32::GUI;
my %DEC;
$DEC{'WAV'} = "temple.wav";
$DEC{'GUI_WIDTH'} = 640;
$DEC{'GUI_HEIGHT'} = 480;
$DEC{'GUI_HEIGHT_MIN'} = 300;
$DEC{'GUI_WIDTH_MIN'} = 400;
$DEC{'WAVGRAPH_X'} = 1;
$DEC{'WAVGRAPH_Y'} = 150;
$DEC{'WAVGRAPH_W'} = 540;
$DEC{'WAVGRAPH_H'} = 165;
$DEC{'PEN_GRAY'} = new Win32::GUI::Pen(-color => [ 125,125,125 ], -wid
+th => 1);
$DEC{'PEN_BLK'} = new Win32::GUI::Pen(-color => [ 0,0,0 ], -width => 1
+);
$DEC{'BRUSH_GRAY'} = new Win32::GUI::Brush([ 125,125,125 ]);
$DEC{'BRUSH_BLK'} = new Win32::GUI::Brush([ 0,0,0 ]);
my $desk = Win32::GUI::GetDesktopWindow();
my $dw = Win32::GUI::Width($desk);
my $dh = Win32::GUI::Height($desk);
my $x = ($dw - $DEC{'GUI_WIDTH'}) / 2;
my $y = ($dh - $DEC{'GUI_HEIGHT'}) / 2;
my $MW = new Win32::GUI::Window(-left => $x, -top=> $y, -helpbutton =>
+ 0, -menu => 0, -maximizebox => 0, -minimizebox => 0, -resizable => 1
+, -topmost => 0, -width => $DEC{'GUI_WIDTH'}, -height => $DEC{'GUI_HE
+IGHT'}, -name => "Window", -text => "$ENV{'PROGRAM_NAME'}");# -intera
+ctive => 0, -onMouseMove => \&MouseMove, -onMouseUp => \&MouseUp, -on
+MouseDown => \&MouseDown);
my $SoundFileGroup = $MW->AddGroupbox(-left => 7, -top => (30), -text 
+=> "Sound File ", -name => "SoundFileGroup",-height => 100, -width  =
+> 385);
my $GraphGroup = $MW->AddGroupbox(-left => 7, -top => (140), -text => 
+"Sound Graph ", -name => "GraphGroup",-height => 265, -width  => 385)
+;
my $PlotButton = $MW->AddButton(-left => 325, -top => 50, -name => 'Pl
+otButton', -text => 'Graph');
my $ListenButton = $MW->AddButton(-left => 325, -top => 80, -name => '
+ListenButton', -text => 'Listen');
my $WavFile = $MW->AddTextfield(-name => "WavFile", -tabstop => 6, -au
+tovscroll => 0, -text => "$DEC{'WAV'}", -sunken => 1, -left => 20, -t
+op => 50, -width => 300, -height => 22, -multiline => 0);
##################EDIT
#######################
my $DC = $MW->GetDC;
$MW->Show();
Win32::GUI::DoEvents();
Win32::GUI::DoEvents();
Win32::GUI::Dialog();
sub ListenButton_Click{
    &PlotButton_Click;
    my $left = $MW->GraphGroup->Left+4;
    my $top = $MW->GraphGroup->Top+15;
    my $width = $MW->GraphGroup->Width-10;
    my $height = ($MW->GraphGroup->Height-21);
    $DC->SelectObject($DEC{'PEN_BLK'});
    my %WV = &DescribeWav($DEC{'WAV'});
    my $LPS = int(($width/$WV{'Duration'}));
    $width = $LPS;
    $WAV = new Win32::Sound::WaveOut($DEC{'WAV'});
    $WAV-> Play() or die "Play: $!";
    for(my $cnt = 0; $cnt <= ($WV{'TotalSamples'});$cnt++){
        my $at = $WAV->Position();
        if ($WV{'nChannels'} > 1){$at = $at*2;}
        if ($WV{'nBitsPerSample'} > 8){$at = $at*2;}
        $WV{'at'} = $at;
        my $gper = int(($at*($MW->GraphGroup->Width-10))/($WV{'TotalSa
+mples'}*$WV{'nBlockAlign'}));
        $DC->BeginPath();
        $DC->MoveTo($left+$gper,$top);
        $DC->LineTo($left+$gper,$top+5);
        $DC->MoveTo($left+$gper,$top+$height-5);
        $DC->LineTo($left+$gper,$top+$height);
        $DC->EndPath();
        $DC->StrokePath();
        if ($WAV->Status() == 1){$cnt = $WV{'TotalSamples'}+1;}
    }
    $WAV->Reset();
    Win32::GUI::DoEvents();
}######################################## end ListenButton_Click
sub PlotWav{
    my $wav = $_[0];
    my $left = $_[1];
    my $top = $_[2];
    my $width = $_[3];
    my $height = $_[4];
    my $secbarheight = ($height*20)/200;
    my $subsecbarheight = ($height*10)/200;
    my %WAV = &DescribeWav($wav);
    $MW->GraphGroup->Text("$WAV{'FileName'} : $WAV{'FileDescription'} 
+: Samples=$WAV{'TotalSamples'} Dur=$WAV{'Duration'}");
    $MW->InvalidateRect(1);
    Win32::GUI::DoEvents();
    $DC->SelectObject($DEC{'PEN_GRAY'});
    $DC->BeginPath();
    $DC->MoveTo($left,$top);
    $DC->LineTo(($left+$width),$top);
    $DC->LineTo(($left+$width),($top+$height));
    $DC->LineTo($left,($top+$height));
    $DC->LineTo($left,$top);
    $DC->EndPath();
    $DC->StrokePath();
    ####### PLOT SECONDS BAR
    my $LPS = int(($width/$WAV{'Duration'}));
    for(my $sec = 0;$sec <= $WAV{'Duration'};$sec++){
        $DC->BeginPath();
        $DC->MoveTo(($left+($sec*$LPS)),$top);
        $DC->LineTo(($left+($sec*$LPS)),$top+$secbarheight);
        $DC->MoveTo(($left+($sec*$LPS)),$top+$height-$secbarheight);
        $DC->LineTo(($left+($sec*$LPS)),$top+$height);
        $DC->EndPath();
        $DC->StrokePath();
    }
    ####### PLOT SUBSECONDS BAR
    $DEC{'PEN_SUB'} = new Win32::GUI::Pen(-color => [ 200,200,200 ], -
+width => 1);
    $DC->SelectObject($DEC{'PEN_SUB'});
    for(my $sec = 0;$sec <= $WAV{'Duration'};$sec++){
        for(my $subsec = 1;$subsec < 10;$subsec++){
            my $asec = $sec+($subsec/10);
            if ($asec <= $WAV{'Duration'}){
                $DC->BeginPath();
                $DC->MoveTo(($left+($asec*$LPS)),$top+1);
                if ($asec =~ /\.5/){
                    $DC->LineTo(($left+($asec*$LPS)),$top+($subsecbarh
+eight+($subsecbarheight/2)));
                    $DC->MoveTo(($left+($asec*$LPS)),$top+$height-($su
+bsecbarheight+($subsecbarheight/2)));
                    $DC->LineTo(($left+($asec*$LPS)),$top+$height);
                    }else{
                        $DC->LineTo(($left+($asec*$LPS)),$top+$subsecb
+arheight+1);
                        $DC->MoveTo(($left+($asec*$LPS)),$top+$height-
+$subsecbarheight);
                        $DC->LineTo(($left+($asec*$LPS)),$top+$height)
+;
                    }
                    $DC->EndPath();
                    $DC->StrokePath();
                }
            }
        }
        ####### END SECONDS BAR
        $DEC{'PEN_LEFT'} = new Win32::GUI::Pen(-color => [ 250,50,50 ]
+, -width => 1);
        $DC->SelectObject($DEC{'PEN_LEFT'});
        $DC->BeginPath();
        $DC->MoveTo($left,$top+($height/2));
        my $volume = 1;#0-1
        my $SPS = ($LPS*$WAV{'Duration'});
        my %INF;
        $INF{'file'} = $WAV{'FilePath'};
        $INF{'start'} = &GetDataStart($WAV{'FilePath'});
        $INF{'nBlockAlign'} = $WAV{'nBlockAlign'};
        $INF{'nBitsPerSample'} = $WAV{'nBitsPerSample'};
        for(my $x = 1;$x<=$width;$x=$x+1){
            $INF{'sample'} = int(($WAV{'TotalSamples'}*$x)/$SPS);
            my ($alc,$arc) = &GetSample(%INF);
            my $lx = $alc;
            if ($INF{'nBitsPerSample'} == 8){$lx = int(($lx-128)*(($he
+ight/4))/255);}
            if ($INF{'nBitsPerSample'} == 16){$lx = int($lx*(($height/
+2))/65335);}
            $DC->LineTo(($left+$x),$top+($height/4)+$lx);
        }
        $DC->EndPath();
        $DC->StrokePath();
        Win32::GUI::DoEvents();
        ################ RIGHT CHANNEL
        $DEC{'PEN_RIGHT'} = new Win32::GUI::Pen(-color => [ 50,250,50 
+], -width => 1);
        $DC->SelectObject($DEC{'PEN_RIGHT'});
        $DC->BeginPath();
        $DC->MoveTo($left,$top+($height/2));
        my $volume = 1;#0-1
        my $SPS = ($LPS*$WAV{'Duration'});
        my %INFR;
        $INFR{'file'} = $WAV{'FilePath'};
        $INFR{'start'} = &GetDataStart($WAV{'FilePath'});
        $INFR{'nBlockAlign'} = $WAV{'nBlockAlign'};
        $INFR{'BPS'} = $WAV{'nBitsPerSample'};
        for(my $x = 1;$x<=$width;$x=$x+1){
            $INFR{'sample'} = int(($WAV{'TotalSamples'}*$x)/$SPS);
            my ($alc,$arc) = &GetSample(%INFR);
            my $rx = $arc;
            if ($INF{'nBitsPerSample'} == 16){$rx = int($rx*(($height/
+2))/65335);}
            $DC->LineTo(($left+$x),$top+(($height/4)*3)+$rx);
        }
        $DC->EndPath();
        $DC->StrokePath();
        Win32::GUI::DoEvents();
    }######################################## end PlotWav
sub PlotButton_Click {
    $DEC{'PEN_L'} = new Win32::GUI::Pen(-color => [ 33,123,33 ], -widt
+h => 1);
    $DEC{'PEN_R'} = new Win32::GUI::Pen(-color => [ 123,33,33 ], -widt
+h => 1);
    $DEC{'WAV'} = $MW->WavFile->Text();
    my %PLOT = @_;
    $MW->InvalidateRect(1);
    Win32::GUI::DoEvents();
    undef $WAV;
    my $left = $MW->GraphGroup->Left+4;
    my $top = $MW->GraphGroup->Top+15;
    my $width = $MW->GraphGroup->Width-10;
    my $height = ($MW->GraphGroup->Height-21);
    my $secbarheight = ($height*20)/200;
    my $subsecbarheight = ($height*10)/200;
    &PlotWav($DEC{'WAV'},$left,$top,$width,$height);
}######################################## end PlotButton_Click
sub Window_Terminate {
    $DEC{'TERMINATING'} = 1;
    return -1;
}######################################## end Window_Terminate
sub Window_Resize {
    if ($MW->Width < $DEC{'GUI_WIDTH_MIN'}){$MW->Width($DEC{'GUI_WIDTH
+_MIN'});}
    if ($MW->Height < $DEC{'GUI_HEIGHT_MIN'}){$MW->Height($DEC{'GUI_HE
+IGHT_MIN'});}
    $DEC{'GUI_WIDTH'} = $MW->Width;
    $DEC{'GUI_HEIGHT'} = $MW->Height;
    $MW->SoundFileGroup->Width($DEC{'GUI_WIDTH'}-20);
    $MW->GraphGroup->Width($DEC{'GUI_WIDTH'}-20);
    $MW->GraphGroup->Height($DEC{'GUI_HEIGHT'}-180);
}######################################## end Window_Resize
sub DescribeWav{
    my $file = $_[0];
    my %WAV;
    my $file_size = int(-s "$file");
    local $/ = \1;
    my $sp;
    my $rcnt;
    my $str;
    my $firChunkCnt = 0;
    my $headChunk = "";
    my $dataChunk = "";
    my $isDataChunk;
    my %chunks;
    $WAV{'FilePath'} = $file;
    $WAV{'FileName'} = $file;
    $WAV{'FileSize'} = $file_size;
    $WAV{'FileName'} =~ s/.*[\\|\/]+//;
    while ($sp <= $file_size){
        open (FILE,$file);
        seek(FILE,$sp,0);
        while (my $li = <FILE>){
            $rcnt++;
            $str .= $li;
            if ($rcnt == 12){
                if (!$str =~ /RIFF.{4}WAVE/){print "Not a WAVE Chunk H
+eader\n";return;}
                $str =~ s/RIFF(.{4})WAVE/$1/i;
                $str = "";
            }
            if (($rcnt > 12)&&($li eq "f")&&(!$firChunkCnt)){$firChunk
+Cnt = $rcnt;}
            if ($firChunkCnt){
                $headChunk .= $li;
                if (length $headChunk == 23){
                    if ($headChunk =~ /fmt\W/){
                        $headChunk =~ s/^(....)(....)(..)(..)(....)(..
+.)(..)(..)/$1\|$2\|$3\|$4\|$5\|$6\|$7\|$8/;
                        my($ckID,$nChunkSize,$wFormatTag,$nChannels,$n
+SamplePerSec,$nAvgBytesPerSec,$nBlockAlign,$nBitsPerSample) = split(/
+\|/,$headChunk);
                        $WAV{'nChunkSize'} = unpack("s",$nChunkSize);
                        $WAV{'wFormatTag'} = unpack("v*",$wFormatTag);
                        $WAV{'nChannels'} = unpack("v*",$nChannels);
                        $WAV{'nSamplePerSec'} = unpack("v*",$nSamplePe
+rSec);
                        $WAV{'nAvgBytesPerSec'} = unpack("v*",$nAvgByt
+esPerSec);
                        $WAV{'nBlockAlign'} = int(unpack("H*",$nBlockA
+lign));
                        $WAV{'nBitsPerSample'} = int(hex(unpack("H*",$
+nBitsPerSample)));
                        if ($WAV{'wFormatTag'} == 1){my @mss = ("mono"
+,"stereo");$WAV{'FileDescription'} = $WAV{'nBitsPerSample'}."bit ".$m
+ss[$WAV{'nChannels'}-1]." ".($WAV{'nSamplePerSec'}/1000)."hz";}
                        $headChunk = "";
                        $WAV{'VALID'} = 1;
                        if ($WAV{'wFormatTag'} != 1){$WAV{'VALID'} = 0
+;}
                        $sp = $file_size+1;
                        $WAV{'DataChunkStart'} = ($WAV{'nChunkSize'}+$
+firChunkCnt+7)+8;
                        last;
                    }
                }
            }
        }
        close FILE;
    }
    $sp = int($WAV{'DataChunkStart'}-8);
    $rcnt = 0;
    $str = "";
    open (FILE,$file);
    while ($sp < $file_size){
        seek(FILE,$sp,0);
        while (my $li = <FILE>){
            $rcnt++;
            $str .= $li;
            if ($rcnt > 8){
                $str =~ s/(....)/$1\|/;
                my($name,$lg) = split(/\|/,$str,2);
                $lg = unpack("V",$lg);
                if (($name eq "data")&&(!$WAV{'SoundChunkStart'})){
                    $WAV{'SoundChunkStart'} = $sp+8;
                    $WAV{'SoundChunkLength'} = $lg;
                    $WAV{'TotalSamples'} = int($WAV{'SoundChunkLength'
+}/$WAV{'nBlockAlign'});
                    $WAV{'Duration'} = $WAV{'TotalSamples'}/$WAV{'nSam
+plePerSec'};
                }
                $chunks{$name} = $lg.":".($rcnt+$sp);
                $rcnt = 0;$str = "";$sp = $sp+$lg+8;last;
            }
        }
    }close FILE;
    foreach my $li(keys %chunks){
        $WAV{"Chunk$li"} = $chunks{$li};
        $WAV{'TotalChunks'}++;
    }
    return %WAV;
}############################################# end DescribeWav
sub GetSample{
    my %INF = @_;
    my $asp = $INF{'start'}+(($INF{'sample'}-1)*$INF{'nBlockAlign'});
    local $/ = \1;
    open(FILE,$INF{'file'});
    binmode FILE;
    seek(FILE,$asp,0);
    my $cnt;
    my $lis;
    while (my $li = <FILE>){
        $lis .= $li;
        if (length $lis >= $INF{'nBlockAlign'}){
            my ($lc,$rc);
            #1=8 bit mono,2=8bitstereo or 16bitmono,4=16bitstereo
            if ($INF{'nBlockAlign'} == 1){
            $lc = hex(unpack("H*",$lis));}
            if ($INF{'nBlockAlign'} == 2){
                if ($INF{'nBitsPerSample'} == 16){
                    $lc = unpack("s",$lc);
                    }else{
                        ($lc,$rc) = unpack('a1a1', $lis);
                        $lc = hex(unpack("H*",$lc));
                        $rc = hex(unpack("H*",$rc));
                    }
                }###############################
                if ($INF{'nBlockAlign'} == 4){
                    ($lc,$rc) = unpack('a2a2', $lis);
                    $lc = unpack("s",$lc);$rc = unpack("s",$rc);
                }###############################
                close FILE;
                local $/ = \n;
                if (!$lc){$lc = 0;}
                if (!$rc){$rc = 0;}
                return $lc,$rc;
            }
        }
    }################################ end GetSample
sub GetDataStart{
    my $file = $_[0];
    my $str;
    local $/ = \1;
    open(FILE,$file);
    binmode FILE;
    seek(FILE,0,0);
    while (my $li = <FILE>){
        $str .= $li;
        if ($str =~ /data$/){
            close FILE;
            local $/ = \n;
            return length($str)+4;
        }
    }
    close FILE;
}################################ end GetDataStart