Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Plot a 8/16bit wav file with Win32::GUI

by true (Pilgrim)
on Jul 27, 2005 at 01:00 UTC ( [id://478426]=sourcecode: print w/replies, xml ) Need Help??
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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://478426]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (3)
As of 2025-07-20 02:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.