#!/usr/bin/perl                        
use Gtk2 -init;    # load the Gtk-Perl module - see <man Gtk2>
use Glib;
use Gtk2::Pango;   # to change fonts
use strict;        # a good idea for all non-trivial Perl scripts
use warnings;      # force print warnings

use Encode;        # You can find Encode at "perl-libintl-perl" package.
                   # Format for Input/Output character - interfaces between Perl's strings and
                   # the rest of the system - see <man Encode>. 

use CDDB_get qw(get_cddb get_discids);  # To get informations from audio cds - See <man CDDB_get>

# Audio::CD is best than cdplay
# imports "Audio::CD" into current package if it exist 
my $use_audiocd = 0;
sub modules { # from Perl Cookbook, chapter 12.
   if (eval "use Audio::CD; 1"){ $use_audiocd = 1;}
   # else{ warn "Couldnt use Audio::CD \n $@";   } 
   return $use_audiocd;
}
modules();	   
		   
my $character = 'iso-8859-1';	# also known as Latin1

$|++;   # Disable buffering by setting the perl-filehandle variable $| to a true value

set_locale Gtk2;  # internationalize

# gtk+2.0 - 2.4.9 -9mdk  and  perl-Glib-1.054
# examples: perl /usr/share/doc/perl-Gtk2-1.054/gtk-demo/main.pl
# See:  man perl, perlfunc, perlintro, Gtk2, Gtk2::Window, ...

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.

# "Free Software, since you might like to learn a bit in the
# process by looking at the code (or teach the author of the program how
# to write better programs...)."

# Copyright 2005 - Claudio Fernandes de Souza Rodrigues
# email: claudiofsr@yahoo.com

# DISCLAIMER
# The author assumes no responsibility for correct function, appropriate use, or misuse 
# of this program, and any damage its function, use or misuse may cause.

# Pensamento filosfico:
# "A calcinha pode no ser a melhor coisa do mundo, mas est bem perto! "

##########------------Global-Variables------------##########
###------------------------------------------------------###

my $VERSION = "0.24";
my $AUTHOR = "Claudio Fernandes de Souza Rodrigues";
my $DATE = "April 3th 2005";
my $HOMEPAGE = "http://gnormalize.sourceforge.net";

#----------------------------------------------------------#
#-------------------- Change-this-path --------------------#
# Change the cdrom drive to the correct path.
my $audiodevide_path ="/dev/cdrom";
#----------------------------------------------------------#
my $quality = 3; # encoder algorithms process for lame.
#----------------------------------------------------------#
my $home = "$ENV{HOME}";
# my $date = system("date +%Y");
my $false = 0; my $true = 1; # convenience variables for true and false
my $tooltips; #$tooltips->set_delay( 1000 ); # 1s of wait
my $notebook;
my $button; my $spinner;my $spinner_q; my $spinner_bitrate_mp3; 
my $spinner_bitrate_ogg; my $value;
my $vbox; my $vbox1; my $vbox2; my $button_save_tag;
my $frame1; my $frame_down;
my $table1; my $table_down;
my $type = "Fix"; my $num_d;
my $file_dialog; my $button_clear;
my $label_type; my $label_VB; 
my $spinner_V; my $spinner_Vogg; my $spinner_Vmpc;
my $label_mp3b; my $combo;
my $label_Min; my $combo_Min; my $vb_Min=64;
my $label_Max; my $combo_Max; my $vb_Max=320;
my $combo_const_avg; my $combo_bitrate;
my $file_mp3=""; my $file_ogg=""; my $file_mpc=""; my $file_ape=""; 
my $file_flac=""; my $file_wav=""; my $file_wav_copy="";
my $spinner_compress; my $label_comp;
my $spinner_compress_flac;
my $directory; my $directory_final;
my $check;  my $label;
my $pbar; my $pbar_n; my $pbar_lame;
my $symbol; my $align; my $separator; my $table;
my $orientation; my $new_val; my $ripper = 'cdparanoia';
my $entry_l; my $entry_n; my $combo_mode; my $combo_fq; my $entry;
my $status_bar; my $context_id = 0; my $adjust; my @music_cda; my @music_sector;
my $already_normalized; my $file_extension = 'other'; my $file_output = 'mp3';
my $bitrate_constant = 160; my $bitrate_avr_mp3 = 160; my $bitrate_avr_ogg = 160; 
my $bitrate_original; my $bitrate_average = $true;
my $pid_lame_decode = -1; my $pid_lame_encode = -1; my $pid_rip_cda = -1;
my $pid_mpc_decode = -1; my $pid_mpc_encode = -1; my $pid_cdpar_rip = -1;
my $pid_normalize = -1; my $pid_ogg_decode = -1; my $pid_ogg_encode = -1;
my $pid_ape = -1; my $pid_flac_decode = -1;
my $V = 4; my $Vogg = 4; my $Vmpc = 5; my $encode="average";# constant, average or variable
my @array_wav; my @array_ogg; my @array_of_files; #array of mp3/ogg/wav 
my $textview; my $scrolled; my $buffer; my $iter; my $track;
my $vbox_text; my $hbox_text; my $frame_debug; my $count = 0; my $timer; 
my $encode_remenber_mp3 = 'average'; my $encode_remenber_ogg = 'average';
my $encode_remenber_mpc = 'variable'; my $input = "File";
my $check_button_del_wav; my $check_button_cddb; my $spinner_pri;
my $go_back = $false; my $go_forward = $false; 
my $delete_wave = $true; my $discDB = $true; my $priority = 15;
my $change_properties = $false; my $check_button_change; my $check_button_overwrite;
my $overwrite = $false; my $volume_normalize = 30; my $insensibility = 0.5; my $spinner_sensi;
my $mode = 'joint-stereo'; my $freq = 44.1; my $comp_ape = 2000; my $comp_flac = 3;
my $combo_rip; my $combo_char; my $entry_cda;
my $button_cdda1; my $button_cdda2; my $button_cdda3;
my $paranoia = $false; my $paranoia_disable = $false; my $paranoia_no_verify = $false;	
my $button_file; my $button_dir; my $change_fonte = $true; my $change_fonte_norm = $true;
#---cdparanoia options
my $disable_paranoia = $true; my $disable_extra_paranoia = $false;
my $abort_on_skip = $false; my $never_skip_repair = $false;
my $button_noia1; my $button_noia2; my $button_noia3; my $button_noia4;
# The paths are automatically found.
my $lame_path; my $oggenc_path; my $mpcenc_path; my $ape_path; my $flac_path;
my $metaflac_path; my $normalize_path; my $oggdec_path; my $mpcdec_path;
my $cdparanoia_path; my $cdda2wav_path;
my $ogginfo_path; my $vorbiscomment_path; my $cdplay_path;
my $cdcd_path;
#---mp3/ogg tag
my $channel; my $mode_original; 
my $mp3_comment; my $mp3_genre; my $mp3_freq; my $mp3_time; my $mp3_title; 
my $mp3_artist; my $mp3_tn; my $total_track; my $mp3_album; my $mp3_year; my $mp3_mpeg;
# ---- More global variables ---- cdrom player #
my $force_unselect_all_tracks = $false;
my $show_time_remaining = $false;
my $playing_audio_cd = $false;
my $selected_track = undef; 
my $total_time = 0;
my @tracks_to_play = ();
my @tracks_already_played = ();
my @tracks_already_played_time = ();
my $play_random = $false; my $loop_tracks = $false;
my @first_sector;
my $pause = $false;

#######------------- Read the config file -----------#######

sub read_config {
	my $config = $home."/.gnormalize_config";
	if( -r "$config" ) {		
	   # read config file
	   open(CONFIG, "$config" ); 
	   while( <CONFIG> ) 
	   {   
	      if ($_ =~ /^cdrom=(.*)/){ $audiodevide_path = $1; }
	      if ($_ =~ /^input=(.*)/){ $input = $1; } 
	      if ($_ =~ /^file_output=(.*)/){ $file_output = $1; }	      
	      if ($_ =~ /^normalization_type=(.*)/){ $type = $1; } 
	      if ($_ =~ /^volume_normalize=(.*)/){ $volume_normalize = $1; } 
	      if ($_ =~ /^insensibility=(.*)/){ $insensibility = $1; } 	      	      
	      if ($_ =~ /^delete_wave=(.*)/){ $delete_wave = $1; } 
	      if ($_ =~ /^discDB=(.*)/){ $discDB = $1; } 
	      if ($_ =~ /^priority=(.*)/){ $priority = $1; } 
	      if ($_ =~ /^change_properties=(.*)/){ $change_properties = $1; } 
	      if ($_ =~ /^overwrite=(.*)/){ $overwrite = $1; } 
	      if ($_ =~ /^character=(.*)/){ $character = $1; }
	      if ($_ =~ /^ripper=(.*)/){ $ripper = $1; }	      
	      if ($_ =~ /^encode=(.*)/){ $encode = $1; } 
	      if ($_ =~ /^bitrate=(.*)/){ $bitrate_constant = $1; } 
	      if ($_ =~ /^bitrate_avr_mp3=(.*)/){ $bitrate_avr_mp3 = $1; }
	      if ($_ =~ /^bitrate_avr_ogg=(.*)/){ $bitrate_avr_ogg = $1; }
	      if ($_ =~ /^bitrate_vrb_mp3=(.*)/){ $V = $1; }
	      if ($_ =~ /^bitrate_vrb_ogg=(.*)/){ $Vogg = $1; }
	      if ($_ =~ /^bitrate_vrb_mpc=(.*)/){ $Vmpc = $1; }	      
	      if ($_ =~ /^comp_ape=(.*)/){ $comp_ape = $1; }
	      if ($_ =~ /^comp_flac=(.*)/){ $comp_flac = $1; }	             	      
	      if ($_ =~ /^mode=(.*)/){ $mode = $1; }	      
	      if ($_ =~ /^Min=(.*)/){ $vb_Min = $1; }
	      if ($_ =~ /^Max=(.*)/){ $vb_Max = $1; }
	      if ($_ =~ /^freq=(.*)/){ $freq = $1; }	      
	      if ($_ =~ /^quality=(.*)/){ $quality = $1; }        	      
	      if ($_ =~ /^disable_paranoia=(.*)/){ $disable_paranoia = $1; }
	      if ($_ =~ /^never_skip_repair=(.*)/){ $never_skip_repair = $1; }
	      if ($_ =~ /^disable_extra_paranoia=(.*)/){ $disable_extra_paranoia = $1; }
	      if ($_ =~ /^abort_on_skip=(.*)/){ $abort_on_skip = $1; } 	      
	      if ($_ =~ /^paranoia=(.*)/){ $paranoia = $1; }
	      if ($_ =~ /^paranoia_disable=(.*)/){ $paranoia_disable = $1; }
	      if ($_ =~ /^paranoia_no_verify=(.*)/){ $paranoia_no_verify = $1; } 
	      if ($_ =~ /^play_random=(.*)/){ $play_random = $1; } 
	      if ($_ =~ /^loop_tracks=(.*)/){ $loop_tracks = $1; }	 	      
	   }
	   close(CONFIG);	
	}
	else { print "Make config file: $config\n"; }
}


#######------------- Save the config file -----------#######

# save configurations when quit the program
sub save_config {
	my $config = $home."/.gnormalize_config";
	# test_expr ? if_true_expr : if_false_expr	
	open(CONFIG, "> $config " ) || die "Can not write to $config: $!";
	  print CONFIG "   gnormalize - $VERSION\n";
	  print CONFIG "author: $AUTHOR\n";
	  print CONFIG "homepage: $HOMEPAGE\n\n";
	  print CONFIG "cdrom=",$entry_cda->get_text,"\n";
	  print CONFIG "input=$input\n";
	  print CONFIG "file_output=$file_output\n";	  
	  print CONFIG "normalization_type=$type\n";
	  print CONFIG "volume_normalize=",$spinner->get_value,"\n";
	  print CONFIG "insensibility=",$spinner_sensi->get_value,"\n";
	  print CONFIG "delete_wave=",$check_button_del_wav->get_active?1:0,"\n";
	  print CONFIG "discDB=",$check_button_cddb->get_active?1:0,"\n";
	  print CONFIG "priority=",$spinner_pri->get_value,"\n";
	  print CONFIG "change_properties=",$check_button_change->get_active?1:0,"\n";
	  print CONFIG "overwrite=",$check_button_overwrite->get_active?1:0,"\n";
	  print CONFIG "character=",$combo_char->entry->get_text,"\n";
	  print CONFIG "ripper=",$combo_rip->entry->get_text,"\n";	  
	  print CONFIG "encode=",$combo_const_avg->entry->get_text,"\n";	  	  	  
	  print CONFIG "bitrate=$bitrate_constant\n";
	  print CONFIG "bitrate_avr_mp3=$bitrate_avr_mp3\n";
	  print CONFIG "bitrate_avr_ogg=$bitrate_avr_ogg\n";
	  print CONFIG "bitrate_vrb_mp3=",$spinner_V->get_value,"\n";
	  print CONFIG "bitrate_vrb_ogg=",$spinner_Vogg->get_value,"\n";
	  print CONFIG "bitrate_vrb_mpc=",$spinner_Vmpc->get_value,"\n";  
	  print CONFIG "comp_ape=",$spinner_compress->get_value,"\n";
	  print CONFIG "comp_flac=",$spinner_compress_flac->get_value,"\n";	 	  
	  print CONFIG "mode=",$combo_mode->entry->get_text,"\n";	  
	  print CONFIG "Min=",$combo_Min->entry->get_text,"\n";
	  print CONFIG "Max=",$combo_Max->entry->get_text,"\n";
	  print CONFIG "freq=",$combo_fq->entry->get_text,"\n";		  
	  print CONFIG "quality=$quality\n";	   	  		  
	  print CONFIG "\n---cdparanoia options:\n";
	  print CONFIG "disable_paranoia=",$button_noia1->get_active?1:0,"\n";
	  print CONFIG "never_skip_repair=",$button_noia2->get_active?1:0,"\n";
	  print CONFIG "disable_extra_paranoia=",$button_noia3->get_active?1:0,"\n";
	  print CONFIG "abort_on_skip=",$button_noia4->get_active?1:0,"\n";
	  print CONFIG "\n---cdda2wav options:\n";	  
	  print CONFIG "paranoia=",$button_cdda1->get_active?1:0,"\n";
	  print CONFIG "paranoia_disable=",$button_cdda2->get_active?1:0,"\n";
	  print CONFIG "paranoia_no_verify=",$button_cdda3->get_active?1:0,"\n\n";
	  print CONFIG "play_random=$play_random\n"; 
	  print CONFIG "loop_tracks=$loop_tracks\n";
	close CONFIG;
}

read_config(); 
#save_config(); # save when quit; see 'sub Quit'
#print "--> bitrate_avr = $bitrate_avr\n";

#######------------------- Icon --------------------########

# from /usr/share/doc/perl-Gtk2-1.054/gtk-demo/main.pl
sub set_icon {
  my $pixbuf;
  eval { $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file("/usr/share/icons/gnormalize.png"); };
  #eval { $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file("/usr/share/icons/wolf.png"); };
  # "/usr/share/icons/sound_section.png"
  if ($pixbuf) {
    # The icon has a white background, make it transparent 
    my $transparent = $pixbuf->add_alpha ($true, 0xff, 0xff, 0xff);

    # only one item on the parameter list, but the parameter list is a list
    Gtk2::Window->set_default_icon_list ($transparent);
    #Gtk2::Window->set_default_icon_list ($pixbuf);
  }
}

set_icon ();

#----------------------------------------------------------#

# Get the parameters from the command line.
# my $file_param = shift;
# if ( not defined($file_param) ){ $file_param = "";}
# print "file = $file_param \n";

#############----------Main--Window----------###############
#####--------------------------------------------------#####
#######-------------------Initial-------------------########

# main window - tela principal
my $window = new Gtk2::Window( "toplevel" );  # principal janela
$window->signal_connect( "delete_event", \&Quit );
$window->set_border_width( 1 );
$window->set_decorated ($true);
# Realization is necessary when you want a widget to use 
# a GdkWindow to access the X server.
$window->realize; 
$window->set('allow-grow'=>$false);
#$window->set_resizable($false);
# 'center'; 'none'; 'mouse' ; (x,y) - window should be placed on the screen.
#$window->set_uposition (300,200);
# see <man Gtk2::Widget>
#$window->set_position ('center');
$window->set_title( "gnormalize - $VERSION" );
#Gtk2::Widget->set_size_request($widget, $width, $height);
#$window->set_size_request(520,440);
#$window->modify_font(Gtk2::Pango::FontDescription->from_string ("Sans 20")); #initial font
#my $color = Gtk2::Gdk::Color->parse ("#B8D3C1"); $window->modify_bg ('normal', $color);
$window->show;

#######----------------------- vbox ----------------########

# "Inside the window we need a vbox to arrange the widgets vertically
# $vbox = new Gtk2::VBox( $homogeneous, $spacing ); 
# $vbox->pack_start( $child, $expand, $fill, $padding ); 
# If the $expand  argument is a true value, then the widgets are laid out 
# in the box to fill in all the extra space in the box so the box is expanded 
# to fill the area allotted to it; otherwise the box is shrunk to just fit 
# the widgets. Setting expand to a false value will allow you to do right 
# and left justification of your widgets. Note that setting $homogeneous  
# to true on the VBox is the same as setting $expand  to true for each widget."

# main vbox 
$vbox = new Gtk2::VBox( $false, 2 );
$vbox->set_border_width( 1 );
$window->add( $vbox );
$vbox->show();

#criamos o vbox1 e o adicionamos dentro do notebook
$vbox1 = new Gtk2::VBox( $false, 2 );
$vbox1->set_border_width( 4 );
$vbox1->show();

#criamos o vbox2 e o adicionamos dentro do notebook
$vbox2 = new Gtk2::VBox( $false, 2 );
$vbox2->set_border_width( 4 );
$vbox2->show();

#criamos o vbox3 e o adicionamos dentro do notebook
my $vbox3 = new Gtk2::VBox( $false, 2 );
$vbox3->set_border_width( 4 );
$vbox3->show();

#criamos o vbox4 e o adicionamos dentro do notebook
my $vbox4 = new Gtk2::VBox( $false, 2 );
$vbox4->set_border_width( 4 );
$vbox4->show();

#criamos o vbox5 e o adicionamos dentro do notebook
my $vbox5 = new Gtk2::VBox( $false, 2 );
$vbox5->set_border_width( 4 );
$vbox5->show();

#####-------------------Notebook-----------------#######
# we put vbox1, vbox2, vbox3, and vbox4 inside notebook
# and we put notebook inside vbox

$notebook = Gtk2::Notebook->new;
$notebook->set( homogeneous => $true );
$notebook->append_page ($vbox1, "data");
$notebook->append_page ($vbox2, "config");
$notebook->append_page ($vbox3, "info");
$notebook->append_page ($vbox4, "rip");
$notebook->append_page ($vbox5, "about");
#$vbox->pack_start( $notebook, 0, 0, 0 );
$vbox->add( $notebook );
$notebook->show;

#################------ItemFactory------################
# Inside the vbox1 we put frame1

###--------------frame1--------------------###
# Adicionamos este frame1 em vbox1
$frame1 = new Gtk2::Frame("normalize: mp3/mpc/ogg/ape/flac/wav");
# Set the frame's label
# Align the label at the right of the frame
$frame1->set_label_align( 1.0, 0.0 );
# Set the style of the frame
$frame1->set_shadow_type( 'etched_out' );
$vbox1->add($frame1);
$frame1->show();

#adicionamos uma tabela dentro do frame
#nesta table1 teremos 3 notaes.
#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
$table1 = new Gtk2::Table (3, 4, 0); 
$table1->set_border_width (4);
$table1->set_row_spacings(8);
$table1->set_col_spacings(4);
$frame1->add( $table1 );
$table1->show;

# on hbox1 we put more two frames: frame11 and frame12
# Gtk2::HBox->new ($homogeneous=0, $spacing=5)
my $hbox1 = Gtk2::HBox->new ($true, 2);
$vbox1->add($hbox1);
$hbox1->show;

###--------------frame11--------------------###
# Adicionamos este frame11 em hbox1
my $frame11 = new Gtk2::Frame("input");
$frame11->set_label_align( 1.0, 0.0 );
$frame11->set_shadow_type( 'etched_out' );
$hbox1->add($frame11);
$frame11->show();

#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
my $table11 = new Gtk2::Table (2, 2, 0); 
$table11->set_border_width (4);
$table11->set_row_spacings(4);
$table11->set_col_spacings(0);
$frame11->add( $table11 );
$table11->show;

###--------------frame12--------------------###
# Adicionamos este frame12 em hbox1
my $frame12 = new Gtk2::Frame("output");
$frame12->set_label_align( 1.0, 0.0 );
$frame12->set_shadow_type( 'etched_out' );
$hbox1->add($frame12);
$frame12->show();

my $table12 = new Gtk2::Table (2, 3, 0); 
$table12->set_border_width (4);
$table12->set_row_spacings(4);
$table12->set_col_spacings(0);
$frame12->add( $table12 );
$table12->show;

###--------------frame2--------------------###
# we put frame2 inside notebook2
my $frame2 = new Gtk2::Frame();
# Align the label at the right of the frame
$frame2->set_label_align( 1.0, 0.0 );
# Set the style of the frame
$frame2->set_shadow_type( 'etched_out' );
#$vbox2->add($frame2);
$frame2->show();

#adicionamos uma tabela dentro do frame
#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
my $table2 = new Gtk2::Table (3, 4, 0); 
$table2->set_border_width (4);
$table2->set_row_spacings(4);
$table2->set_col_spacings(10);
$frame2->add( $table2 );
$table2->show;

###--------------frame201--------------------###
# we put frame201 inside notebook2
my $frame201 = new Gtk2::Frame();
$frame201->set_label_align( 1.0, 0.0 );
$frame201->set_shadow_type( 'etched_out' );
$frame201->show();

#adicionamos uma tabela dentro do frame
#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
my $table201 = new Gtk2::Table (3, 4, 0); 
$table201->set_border_width (4);
$table201->set_row_spacings(4);
$table201->set_col_spacings(8);
$frame201->add( $table201 );
$table201->show;

###--------------frame202--------------------###
# we put frame202 inside notebook2
my $frame202 = new Gtk2::Frame("cdparanoia");
$frame202->set_label_align( 1.0, 0.0 );
$frame202->set_shadow_type( 'etched_out' );
$frame202->show();

#adicionamos uma tabela dentro do frame
#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
my $table202 = new Gtk2::Table (3, 4, 0); 
$table202->set_border_width (4);
$table202->set_row_spacings(4);
$table202->set_col_spacings(8);
$frame202->add( $table202 );
$table202->show;

###--------------frame203--------------------###
# we put frame203 inside notebook2
my $frame203 = new Gtk2::Frame("cdda2wav");
$frame203->set_label_align( 1.0, 0.0 );
$frame203->set_shadow_type( 'etched_out' );
$frame203->show();

#adicionamos uma tabela dentro do frame
#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
my $table203 = new Gtk2::Table (3, 4, 0); 
$table203->set_border_width (4);
$table203->set_row_spacings(4);
$table203->set_col_spacings(8);
$frame203->add( $table203 );
$table203->show;

#-------------------Notebook2----------------#
# we put frame2 inside notebook2
# and we put notebook2 inside vbox2
my $notebook2 = Gtk2::Notebook->new; 
# position = 'left', 'right','top', 'bottom'
$notebook2->set_tab_pos( 'left' );
$notebook2->append_page ($frame2, "option1");
$notebook2->append_page ($frame201, "option2");

if ($ripper eq "cdparanoia"){ $notebook2->append_page ($frame202, "ripper"); }
if ($ripper eq "cdda2wav"){   $notebook2->append_page ($frame203, "ripper"); }

$vbox2->add( $notebook2 );
$notebook2->show;

###--------------frame21--------------------###
# Adicionamos este frame21 em vbox2 (vbox2 est dentro do notebook)
my $frame21 = new Gtk2::Frame("lame settings");
$frame21->set_label_align( 1.0, 0.0 );
$frame21->set_shadow_type( 'etched_out' );
$vbox2->add($frame21);
$frame21->show();

#adicionamos uma tabela21 dentro do frame21
my $table21 = new Gtk2::Table (3, 7, $false); 
$table21->set_border_width (4);
$table21->set_row_spacings(4);
$table21->set_col_spacings(4);
$frame21->add( $table21 );
$table21->show;

###----------------hpaned3-----------------###
###-----------------init-------------------###
# see <man Gtk2::Paned>
# Inside the vbox3 we put hpaned3
# and inside hpaned3 we put frame3 and frame31

my $hpaned3 = Gtk2::HPaned->new;
$vbox3->pack_start ($hpaned3, $true, $true, $false);
$hpaned3->set_border_width (0);
$hpaned3->show;

###--------------frame3--------------------###
my $frame3 = new Gtk2::Frame("tag");
# Align the label at the right of the frame
$frame3->set_label_align( 1.0, 0.0 );
# Set the style of the frame
$frame3->set_shadow_type( 'etched_in' );
$hpaned3->add1 ($frame3);
$frame3->show();

#adicionamos uma tabela dentro do frame
#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
my $table3 = new Gtk2::Table (6, 4, 0);
$table3->set_size_request( 360,-1 ); #width,height; 
$table3->set_border_width (4);
$table3->set_row_spacings(6);
$table3->set_col_spacings(4);
$frame3->add( $table3 );
$table3->show;

###--------------frame31--------------------###
my $frame31 = new Gtk2::Frame("file info");
# Align the label at the right of the frame
$frame31->set_label_align( 1.0, 0.0 );
# Set the style of the frame
$frame31->set_shadow_type( 'etched_in' );
$hpaned3->add2 ($frame31);
$frame31->show();

#adicionamos uma tabela dentro do frame
my $table31 = new Gtk2::Table (6, 1, 0); 
#$table31->set_size_request( 144,-1 ); #width,height
$table31->set_border_width (2);
$table31->set_row_spacings(8);
$table31->set_col_spacings(2);
$frame31->add( $table31 );
$table31->show;

# "If shrink is true, then that child can be made 
# smaller than its requisition by the user."
$hpaned3->child1_shrink($false);
$hpaned3->child2_shrink($true);

###-----------------final------------------###
###----------------hpaned3-----------------###

###--------------frame4--------------------###
# Adicionamos este frame4 em vbox4 (vbox4 est dentro do notebook)
# see: /usr/share/doc/perl-Gtk2-1.054/gtk-demo/list_store.pl
my $sw = Gtk2::ScrolledWindow->new (undef, undef);
$sw->set_shadow_type ('etched-in');
$sw->set_policy ('automatic', 'always');
$vbox4->add($sw);
$sw->show;

my @length = (); # array with song names of audio cd
# create tree model
my $model = create_model (@length);

# create tree view
my $treeview4 = Gtk2::TreeView->new($model);
$treeview4->set_rules_hint ($true);  # draw rows in alternating colors
#$treeview4->set_search_column (COLUMN_TITLE);
$treeview4->get_selection->set_mode ('single'); # default
$treeview4->signal_connect("cursor-changed", \&selection_tree);
$treeview4->set_reorderable ($true);
$treeview4->show;
	 
# add columns to the tree view
$sw->add ($treeview4); 
	
# add columns to the tree view
add_columns ($treeview4);

sub create_model {
        my @array = @_;
	
        use constant COLUMN_RIP       => 0; # for two dimensional array
        use constant COLUMN_TRACK     => 1; # this is the column number
        use constant COLUMN_ARTIST    => 2;
        use constant COLUMN_TITLE     => 3;
        use constant COLUMN_LENGTH    => 4;
        use constant COLUMN_EDITABLE  => 5;
        use constant NUM_COLUMNS      => 6;
	 
        # making array of array references - see <man perllol>
	for (my $i=1;$i<=@array;$i++){ 
	      push @music_cda, {
	                 rip => $true,
	                 track => $i,
			 artist => "artist",
			 title => "music title",
			 length => "$array[$i-1]",
	                 editable => $true,
              };
	}
		
	my $model = Gtk2::ListStore->new ('Glib::Boolean', # => G_TYPE_BOOLEAN
                                          'Glib::Uint',    # => G_TYPE_UINT
					  'Glib::String',  # => G_TYPE_STRING
                                          'Glib::String',  # => G_TYPE_STRING
                                          'Glib::String',  # you get the idea
				          'Glib::Boolean');
	# add data to the list store
        foreach my $d (@music_cda) {
            my $iter = $model->append;
            $model->set ($iter,
		   COLUMN_RIP,      $d->{rip},
                   COLUMN_TRACK,    $d->{track},
		   COLUMN_ARTIST,   $d->{artist},
	           COLUMN_TITLE,    $d->{title},
	           COLUMN_LENGTH,   $d->{length},	
                   COLUMN_EDITABLE, $d->{editable}
            );
        }
  return $model;
}

###-------------------------------------------------###

###--------------frame5--------------------###
# Adicionamos este frame5 em vbox5 (vbox5 est dentro do notebook)
my $frame5 = new Gtk2::Frame();
# Set the frame's label
# Align the label at the right of the frame
$frame5->set_label_align( 1.0, 0.0 );
# Set the style of the frame
$frame5->set_shadow_type( 'etched_out' );
$vbox5->add($frame5);
$frame5->show();

###-------------------------------------------------###

###------------------frame_normalize----------------###

# we put this $frame_normalize inside the vbox
# Adicionamos este $frame_normalize em vbox
my $frame_normalize = new Gtk2::Frame();
$frame_normalize->set_label_align( 1.0, 0.0 );
$frame_normalize->set_shadow_type( 'etched_out' );
$vbox->add( $frame_normalize );
$frame_normalize->show();

# adicionamos um hbox dentro do $frame_normalize
my $hbox_normalize = new Gtk2::HBox;
$hbox_normalize->set_border_width(2);
$frame_normalize->add($hbox_normalize);
$hbox_normalize->show;

###------------------frame_down----------------------###
# we put this frame_down inside the vbox
# Adicionamos este frame em vbox
$frame_down = new Gtk2::Frame();
#$frame_down->set_label( "nornal" );
# Align the label at the right of the frame
$frame_down->set_label_align( 1.0, 0.0 );
# Set the style of the frame
$frame_down->set_shadow_type( 'etched_out' );
#$vbox->pack_start( $frame_down, 0, 0, 0 );
$vbox->add( $frame_down );
$frame_down->show();

#adicionamos uma tabela dentro do frame_down
#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
$table_down = new Gtk2::Table (3, 4, 0); 
$table_down->set_border_width (4);
$table_down->set_row_spacings(4);
$table_down->set_col_spacings(2);
$table_down->set_size_request(-1,100);
$frame_down->add( $table_down );
$table_down->show;

#####------------------T a b l e 1 ------------------#####
# Now, we put the widgets in table1

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "Normalization Type:" );
$label->set_alignment( 1.0, 0.5 );  # right   
$table1->attach ($label, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
$label->show();

#####------------Option-Menu-------------------#####

# see range at gtkperl-tutorial

# A hbox - horizontal box - to put a menu and a button in:
my $box_type = new Gtk2::HBox( $false, 0 );

my $opt_menu = new Gtk2::OptionMenu(); 
$opt_menu->set_size_request( 80, -1 );
my $menu = new Gtk2::Menu();

my $item_fix = new Gtk2::MenuItem( "Fix" );
$item_fix->signal_connect( "activate", \&tra , "Fix" );
$item_fix->show();
$menu->append( $item_fix );

my $item_p = new Gtk2::MenuItem( "Peak" );
$item_p->signal_connect( "activate", \&tra , "Peak" );
$item_p->show();
$menu->append( $item_p );

my $item_mix = new Gtk2::MenuItem( "Mix" );
$item_mix->signal_connect( "activate", \&tra, "Mix" );
$item_mix->show();
$menu->append( $item_mix );

my $item_none = new Gtk2::MenuItem( "None" );
$item_none->signal_connect( "activate", \&tra, "None" );
$item_none->show();
$menu->append( $item_none );

$opt_menu->set_menu( $menu );
$box_type->pack_start( $opt_menu, $true, $true, 0 );
$opt_menu->show();

$table1->attach ($box_type,  1, 2, 0, 1, 'fill', 'shrink', 0, 0);
$box_type->show();

sub tra
{  my ( $item, $name ) = @_; 
   if ($name eq "Fix" ) {$type="Fix";  $spinner->set_sensitive($true );} 
   if ($name eq "Peak") {$type="Peak"; $spinner->set_sensitive($false);}
   if ($name eq "Mix")  {$type="Mix";  $spinner->set_sensitive($false);}
   if ($name eq "None") {$type="None"; $spinner->set_sensitive($false);}
   return $type;
}

#####------------ final of option_menu ----------#####

# Create a centering alignment object;
$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table1->attach_defaults ($align,  2, 3, 0, 1);
$align->show;

# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
my $adj = new Gtk2::Adjustment( $volume_normalize, 1.0, 99.0, 1.0, 5.0, 0.0 );
#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
$spinner = new Gtk2::SpinButton( $adj, 0.2, 0 );
$spinner->set_wrap( $true );
$spinner->set_editable($false);
$spinner->set_update_policy( 'if_valid' );
$tooltips = new Gtk2::Tooltips();
$tooltips->set_tip( $spinner, "Adjust the RMS volume to normalize.");
$align->add($spinner);
$spinner->show();

#-------------------------------------#

if ($type eq "Fix")  { $opt_menu->set_history (0); $spinner->set_sensitive($true );} # 0 set to "Fix"
if ($type eq "Peak") { $opt_menu->set_history (1); $spinner->set_sensitive($false);} 
if ($type eq "Mix")  { $opt_menu->set_history (2); $spinner->set_sensitive($false);} 
if ($type eq "None") { $opt_menu->set_history (3); $spinner->set_sensitive($false);} # 3 set to "None"

#-------------------------------------#

$label = new Gtk2::Label( "Insensibility:" );
$label->set_alignment( 1.0, 0.5 );  # right   
$table1->attach ($label, 3, 4, 0, 1, 'fill', 'shrink', 0, 0);
$label->show();

$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table1->attach ($align,  4, 5, 0, 1, 'fill', 'shrink', 0, 0);
$align->show;

# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
$adj = new Gtk2::Adjustment( $insensibility, 0.2, 5.0, 0.1, 0, 0 );
#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
$spinner_sensi = new Gtk2::SpinButton( $adj, 0.2, 1 );
$spinner_sensi->set_wrap( $false );
$spinner_sensi->set_editable($false);
$spinner_sensi->set_update_policy( 'if_valid' );
$tooltips = new Gtk2::Tooltips();
my $msg = "Insensibility of normalize. Only not re-encode if:\nthe extension of input file is the";
$msg .= " same of output file;\nand change properties button is not active;";
$msg .= "\nand adjust of normalize is less than insensibility value.";
$tooltips->set_tip( $spinner_sensi, $msg);
$align->add($spinner_sensi);
$spinner_sensi->show();

$label = new Gtk2::Label( "dB" );
$label->set_alignment( 0.0, 0.5 );  # right   
$table1->attach ($label, 5, 6, 0, 1, 'fill', 'shrink', 0, 0);
$label->show();

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "Selected File:" );
$label->set_alignment( 1.0, 0.5 );  # right 
$table1->attach ($label, 0, 1, 1, 2, 'fill', 'fill', 0, 0);
$label->show();

# Create the Entry to put the file to normalize
my $entry_f = new Gtk2::Entry( ); 
#$entry_f->set_max_length (100);
$entry_f->set_text( " " );
$tooltips->set_tip( $entry_f, "Select a mp3/mpc/ogg/ape/flac/wav file.");
$entry_f->select_region( 0, length( $entry_f->get_text() ) );
$table1->attach_defaults ($entry_f, 1, 6, 1, 2 );
$entry_f->set_sensitive($true);
$entry_f->show();

#if ( $file_param ne "" ){ $entry_f->set_text($file_param); }


# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "Directory:" );
$label->set_alignment( 1.0, 0.5 );  # right   
$table1->attach ($label, 0, 1, 2, 3, 'fill', 'fill', 0, 0);
$label->show();

# Create the Entry to put the directory path
my $entry_d = new Gtk2::Entry( );
#$entry_d->set_max_length (100);
$directory = "$ENV{'PWD'}/"; #inicializao da varivel
$entry_d->set_text( "$directory" );
#$entry->set_text( "$ENV{'HOME'}/" );
$tooltips->set_tip( $entry_d, "$directory");
$entry_d->select_region( 0, length( $entry_d->get_text() ) );
$table1->attach_defaults ($entry_d, 1, 5, 2, 3 );
$entry_d->set_sensitive($false);
$entry_d->show();

# Create Dir_Selection button
$button = Gtk2::Button->new_from_stock ('gtk-open');
#$button->set_label("Dir / File");
$tooltips->set_tip( $button, "Choose one supported file.");
$button->signal_connect( "clicked", \&dir_selection );
$table1->attach ($button,  5, 6, 2, 3, 'shrink', 'shrink', 0, 0);
$button->show();
# This makes it so the button is the default.
$button->can_default($true);
# This grabs this button to be the default button. Simply hitting the "Enter"
# key will cause this button to activate.
$button->grab_default;
$button->set( 'focus-on-click' => $false, 'relief' => 'none' );


################------Input--------##################
#######------------radiobutton--------------#########

# veja:   man perlnumber  ou perlop ou man overload

# criar um label vazio
$label = new Gtk2::Label( "   " );
$label->set_alignment( 1.0, 0.5 );  # right 
$table11->attach ($label, 0, 1, 0, 1, 'fill', 'fill', 0, 0);
$label->show();

$button_file = new Gtk2::RadioButton(  undef , "File");
$tooltips->set_tip( $button_file, "Normalize only the file choosed");
# $button_file->set_active( $true );  #padro - ativo
$table11->attach_defaults($button_file, 1, 2, 0, 1);
$button_file->signal_connect( "toggled", \&DF );
$button_file->show;

$button_dir = new Gtk2::RadioButton( $button_file, "Directory");
$tooltips->set_tip( $button_dir, "Normalize all files of specific format inside the directory");
$table11->attach_defaults($button_dir, 1, 2, 1, 2);
$button_dir->signal_connect( "toggled", \&DF );
$button_dir->show;  

sub DF {
#dependendo se os botes Directory ou File estiverem ativos, teremos estas opes.
       # SWITCH: { # see  man perlsyn
            if ($button_dir->get_active) { 
			  #print( "Choose: Directory\n" );
			  $entry_f->set_sensitive($false);
			  $entry_d->set_sensitive($true);
			  $input = "Directory";
	    }
	    if ($button_file->get_active) { 
                          #print( "Choose: File\n" );
			  $entry_f->set_sensitive($true);
			  $entry_d->set_sensitive($false);
			  $input = "File";
	    }       	  		  
}

if ($input eq "File"){ $button_file->set_active($true);}
elsif ($input eq "Directory"){ $button_dir->set_active($true);}

################-----Output--------##################
#######------------radiobutton--------------#########

# veja:   man perlnumber  ou perlop ou man overload

my $button_output_mp3 = new Gtk2::RadioButton(  undef , "mp3");
$tooltips->set_tip( $button_output_mp3, "After normalize, the output format will be mp3. Play it with mpg123 or madplay.");
$table12->attach_defaults($button_output_mp3, 0, 1, 0, 1);
$button_output_mp3->signal_connect( "clicked", \&refresh_selection );
$button_output_mp3->show;   

my $button_output_mpc = new Gtk2::RadioButton( $button_output_mp3, "mpc");
$tooltips->set_tip( $button_output_mpc, "After normalize, the output format will be mpc. Play it with mppdec.");
$table12->attach_defaults($button_output_mpc, 1, 2, 0, 1);
$button_output_mpc->signal_connect( "clicked", \&refresh_selection );
$button_output_mpc->show;

my $button_output_ogg = new Gtk2::RadioButton( $button_output_mp3, "ogg");
$tooltips->set_tip( $button_output_ogg, "After normalize, the output format will be ogg. Play it with ogg123.");
$table12->attach_defaults($button_output_ogg, 0, 1, 1, 2);
$button_output_ogg->signal_connect( "clicked", \&refresh_selection );
$button_output_ogg->show;    

my $button_output_ape = new Gtk2::RadioButton( $button_output_mp3, "ape");
$tooltips->set_tip( $button_output_ape, "After normalize, the output format will be ape.");
$table12->attach_defaults($button_output_ape, 1, 2, 1, 2);
$button_output_ape->signal_connect( "clicked", \&refresh_selection );
$button_output_ape->show;  

my $button_output_flac = new Gtk2::RadioButton( $button_output_mp3, "flac");
$tooltips->set_tip( $button_output_flac, "After normalize, the output format will be flac. Play it with flac123.");
$table12->attach_defaults($button_output_flac, 2, 3, 1, 2);
$button_output_flac->signal_connect( "clicked", \&refresh_selection );
$button_output_flac->show; 

##----------------------------------------------------##
#####----------------T a b l e 2 ------------------#####

$check_button_del_wav = Gtk2::CheckButton->new ("delete wave files");
$check_button_del_wav->set_active($delete_wave);
$tooltips->set_tip( $check_button_del_wav, "After the normalize process, the wave files will be deleted.");
#$check_button_del_wav->set_alignment (6,0);
$table2->attach($check_button_del_wav,  0, 1, 0, 1, 'fill', 'fill', 0, 0);
$check_button_del_wav->show;

$check_button_cddb = Gtk2::CheckButton->new ("DiscDB");
$check_button_cddb->set_active($discDB);
my $tips  = "Read the CDDB entry for an audio CD in your drive.";
$tips .= " cddb host: \"freedb.freedb.org\" ";
$tooltips->set_tip( $check_button_cddb, $tips );
$table2->attach($check_button_cddb,  1, 2, 0, 1, 'expand', 'shrink', 0, 0);
$check_button_cddb->show;

$label = new Gtk2::Label( "priority:" );
$label->set_alignment( 1.0, 0.5 ); 
$table2->attach ($label, 2, 3, 0, 1, ['fill','expand'], 'shrink', 0, 0);
$label->show();

# Create a (left, centering) alignment object;
$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table2->attach($align,  3, 4, 0, 1, 'fill', 'shrink', 0, 0);
$align->show;

$adj = new Gtk2::Adjustment( $priority, 0, 19, 1, 0, 0 );
$spinner_pri = new Gtk2::SpinButton( $adj, 0.1, 0 );
$spinner_pri->set_editable($false);
$spinner_pri->set_size_request( 50, -1 );
$spinner_pri->set_update_policy( 'if_valid' );
$spinner_pri->set_wrap( $true );
my $spinner_pri_msg = "Modify scheduling priority: 0 (highest priority) to 19 (lowest)";
$tooltips->set_tip( $spinner_pri, $spinner_pri_msg );
$align->add($spinner_pri);
$spinner_pri->show();

$check_button_change = Gtk2::CheckButton->new ("change properties");
$check_button_change->set_active($change_properties);
$check_button_change->signal_connect( "clicked", \&encode_choice_sensitive );
$tips = "After normalize the files, you can change its enconde properties by";
$tips .= " determining the encode type and bitrate of all output files.";
$tips .= " Otherwise, the bitrate of each input file will be conserved.";
$tooltips->set_tip( $check_button_change, $tips);
$table2->attach($check_button_change,  0, 1, 1, 2, 'fill', 'fill', 0, 0);
$check_button_change->show;

$check_button_overwrite = Gtk2::CheckButton->new ("overwrite");
$check_button_overwrite->set_active($overwrite);
$tips  = "The normalized file will be saved in the same directory of input,";
$tips .= " if it already exist then overwrite the original.";
$tooltips->set_tip( $check_button_overwrite, $tips );
$table2->attach($check_button_overwrite,  0, 1, 2, 3, 'fill', 'fill', 0, 0);
$check_button_overwrite->show;

##----------------------------------------------------##
#####---------------T a b l e 201 -----------------#####

#####--------------------Combo---------------------#####

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "character:" );
$label->set_alignment( 1.0, 0.5 ); 
$table201->attach ($label, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
$label->show();

my @all_encodings = Encode->encodings(":all"); # see <man Encode>
#print ("Encode list = @all_encodings\n");

$combo_char = Gtk2::Combo->new;
$combo_char->set_popdown_strings ( @all_encodings );
$combo_char->entry->set_text($character); #standard value
$combo_char->set_size_request( 124, -1 );
$combo_char->entry->set_editable ($false);
$combo_char->entry->signal_connect("changed", \&char_choice, $combo_char);
$table201->attach($combo_char,  1, 4, 0, 1, ['expand','fill'], 'shrink', 0, 0);
$combo_char->show();

## choice of table of character
sub char_choice
{
	my ($widget,$comboCopy) = @_;
        my $entry=$comboCopy->entry->get_text();
	if ( $entry ne ""){ 
	    $character = $entry;
	    #print (" char = $character\n");
	}
}
#####-------------------final of Combo-------------#####

#####--------------------Combo---------------------#####

$label = new Gtk2::Label( "ripper:" );
$label->set_alignment( 1.0, 0.5 );  
$table201->attach ($label, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
$label->show;

$combo_rip = Gtk2::Combo->new;
$combo_rip->set_popdown_strings ( "cdparanoia", "cdda2wav" );
$combo_rip->entry->set_text( "$ripper" ); #standard value
$combo_rip->entry->set_editable($false); 
$combo_rip->entry->signal_connect("changed", \&ripperChanged, $combo_rip);
$table201->attach ($combo_rip, 1, 4, 1, 2, 'fill', 'shrink', 0, 0);
$combo_rip->show();

## audio mp3 bitrate is changed
sub ripperChanged
{
	my ($widget,$comboCopy) = @_;
        my $entry=$comboCopy->entry->get_text();
	if ( $entry ne ""){
	   $ripper = $entry; # copy the value of bitrate changed
	   if ($ripper eq "cdparanoia"){ 
	      $notebook2->remove_page (2);
	      $notebook2->append_page ($frame202, "ripper");
	   }
	   if ($ripper eq "cdda2wav"){ 
	      $notebook2->remove_page (2);
	      $notebook2->append_page ($frame203, "ripper");
	   }
	}
}

#####-------------------final of Combo-------------#####

# my $widget = Glib::Object->new(  veja histogram 
my $window_bg_color = color_bg($window);
# print "window_bg_color = $window_bg_color \n"; #E6E7E6

# return the background color of widget in hexdecimal format
sub color_bg   # not used
{
   my ( $widget ) = @_;
   my $color;
   my $red;
   my $green;
   my $blue;
 
   # see man Gtk2::Style
   $red = $widget->get_style->bg('normal')->red;
   $green = $widget->get_style->bg('normal')->green;
   $blue = $widget->get_style->bg('normal')->blue;

   # Convert to HTML format.  There are better ways to do this.
   $red = $red / 256;
   $green = $green / 256;
   $blue = $blue / 256;

   # uc: Returns an uppercased version of EXPR.
   # %x   an unsigned integer, in hexadecimal
   $red = uc( sprintf( "%lx", $red ) );
   $green = uc( sprintf( "%lx", $green ) );
   $blue = uc( sprintf( "%lx", $blue ) );

   $red = "0" . $red if ( $red =~ /^\d$/ );
   $green = "0" . $green if ( $green =~ /^\d$/ );
   $blue = "0" . $blue if ( $blue =~ /^\d$/ );
   
   $color = "#$red$green$blue"; #   print "color = $color\n";  # like #E6E7E6
   return $color;
} 

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "gnome font:" );
$label->set_alignment( 1.0, 0.5 );  
$table201->attach ($label, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
$label->show;

# Build the font-button
my $font_name = $window->get_style->font_desc->to_string;
my $button_font = Gtk2::Button->new($font_name);
#$button_font->signal_connect( 'clicked' => sub {window_select_font($_[1]);}, $label);
$button_font->signal_connect( 'clicked', 
sub{ 
   exec_cmd_system("gnome-font-properties");
   $change_fonte = $true;
   $change_fonte_norm = $true;
} );
$table201->attach ($button_font, 1, 4, 2, 3, ['expand','fill'], 'shrink', 0, 0);
$tooltips->set_tip( $button_font, "Advised font: Sans 11");
$button_font->set_sensitive($true);
$button_font->show;

sub window_select_font  # not used
{
	my $widget = shift;

	my $fsd = Gtk2::FontSelectionDialog->new ('Font Selection Dialog');
	$fsd->set_position('mouse');
        
	my $original_font_name = $widget->get_style->font_desc->to_string;
	$fsd->set_font_name ($original_font_name);

	$fsd->signal_connect ('response' => sub {
		my (undef, $response) = @_;
		if ($response eq 'ok') {
		   my $font_name = $fsd->get_font_name;
		   return unless $font_name;
		   print "fontname = $font_name\n";
		   $widget->modify_font(Gtk2::Pango::FontDescription->from_string($font_name));
		   $button_font->set_label($font_name);
		}
		$fsd->destroy;
	});

	$fsd->show;
}

sub change_all_child {
  my ($togglebutton, $container) = @_;
  my $newstate = ! $togglebutton->get_active;

  foreach my $child ($container->get_children) {
      # don't disable our toggle
      $child->set_sensitive ($newstate)
           if $child != $togglebutton;
  }
}

##-------------------------------------------------------##
#####----------------- T a b l e 202 -----------------#####

$button_noia1 = Gtk2::CheckButton->new ("disable paranoia");
$button_noia1->set_active($disable_paranoia);
$tooltips->set_tip( $button_noia1, "Disable  all  data  verification  and correction features. See cdparanoia (option -Z).");
$table202->attach($button_noia1,  0, 1, 0, 1, 'fill', 'shrink', 0, 0);
$button_noia1->show;

$button_noia2 = Gtk2::CheckButton->new ("never skip repair");
$button_noia2->set_active($never_skip_repair);
$tooltips->set_tip( $button_noia2, "Do not accept any skips; retry forever if needed. See cdparanoia (option -z).");
$table202->attach($button_noia2,  1, 2, 0, 1, 'fill', 'shrink', 0, 0);
$button_noia2->show;

$button_noia3 = Gtk2::CheckButton->new ("disable extra paranoia");
$button_noia3->set_active($disable_extra_paranoia);
$msg = "Disables intra-read data verification; only overlap checking at read boundaries is performed. See cdparanoia (option -Y).";
$tooltips->set_tip( $button_noia3, $msg);
$table202->attach($button_noia3,  0, 1, 1, 2, 'fill', 'shrink', 0, 0);
$button_noia3->show;

$button_noia4 = Gtk2::CheckButton->new ("abort on skip");
$button_noia4->set_active($abort_on_skip);
$msg = "If the read skips due to imperfect data, a scratch, whatever, abort reading this track. See cdparanoia (option -X).";
$tooltips->set_tip( $button_noia4, $msg);
$table202->attach($button_noia4,  0, 1, 2, 3, 'fill', 'shrink', 0, 0);
$button_noia4->show;

##-------------------------------------------------------##
#####----------------- T a b l e 203 -----------------#####

$button_cdda1 = Gtk2::CheckButton->new ("paranoia");
$button_cdda1->set_active($paranoia);
$msg = "Use the paranoia library instead of cdda2wav's routines for reading. See cdda2wav (option -paranoia).";
$tooltips->set_tip( $button_cdda1, $msg);
$table203->attach($button_cdda1,  0, 1, 0, 1, 'fill', 'shrink', 0, 0);
$button_cdda1->show;

$button_cdda2 = Gtk2::CheckButton->new ("paranoia disable");
$button_cdda2->set_active($paranoia_disable);
$tooltips->set_tip( $button_cdda2, "Disables paranoia mode. Paranoia is still being used. See cdda2wav (option -paraopts=help).");
$table203->attach($button_cdda2,  1, 2, 0, 1, 'fill', 'shrink', 0, 0);
$button_cdda2->show;

$button_cdda3 = Gtk2::CheckButton->new ("paranoia no-verify");
$button_cdda3->set_active($paranoia_no_verify);
$msg = "Switches verify off, and overlap on.";
$tooltips->set_tip( $button_cdda3, $msg);
$table203->attach($button_cdda3,  0, 1, 1, 2, 'fill', 'shrink', 0, 0);
$button_cdda3->show;

###-----------------------------------------------------###
#####----------------T a b l e 21 --------------------#####

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "encode type:" );
$label->set_alignment( 1.0, 0.5 );   
$table21->attach ($label, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
$label->show();

# Create a centering alignment object;
$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table21->attach($align,  1, 2, 0, 1, 'fill', 'shrink', 0, 0);
$align->show;

#####--------------------Combo---------------------#####
$combo_const_avg = Gtk2::Combo->new;
$combo_const_avg->set_popdown_strings ( "average", "constant", "variable" );
$combo_const_avg->entry->set_text( $encode ); #standard value
$combo_const_avg->set_size_request( 104, -1 );
$combo_const_avg->entry->set_editable ($false);
$combo_const_avg->entry->signal_connect("changed", \&encode_choice, $combo_const_avg);
$align->add($combo_const_avg);
$combo_const_avg->set_sensitive($change_properties);
$combo_const_avg->show();

## encode type: constant, average or variable bitrate
## show or hide some widgets
sub encode_choice
{
	my ($widget,$comboCopy) = @_;
        my $entry=$comboCopy->entry->get_text();
	#print ("  encode type = $entry \n");
	if ( $entry ne ""){ # to retain the last value changed

	  # set the new value for $encode
	  $encode = $entry;
	  encode_choice_sensitive();
	  $status_bar->push($context_id, " output: $file_output with $encode bitrate.");
   
          # set the last encode type
          if ($button_output_mp3->get_active ){$encode_remenber_mp3 = $encode;}
          if ($button_output_ogg->get_active ){$encode_remenber_ogg = $encode;}
          if ($button_output_mpc->get_active ){$encode_remenber_mpc = $encode;}	  
	}
}

# Set widget sensitive according to encode choice
sub encode_choice_sensitive { 
   $encode = $combo_const_avg->entry->get_text(); #refresh $encode value
   $vb_Max = $combo_Max->entry->get_text; #refresh value
   $vb_Min = $combo_Min->entry->get_text;
      
   if ($button_output_mp3->get_active ){
       if ( $check_button_change->get_active){
           if ( $encode eq "constant" ){ encode_sensitive(1,1,0,0,0,0,0,1,0,0,0,0,1,1,1,1,1);}
	   if ( $encode eq "average"  ){ encode_sensitive(1,0,1,0,0,0,1,1,0,0,0,1,1,1,1,2,1);}
	   if ( $encode eq "variable" ){ encode_sensitive(1,0,0,0,0,0,1,1,1,0,0,1,1,1,1,0,1);}
       }	 
       else{ 
           if ( $encode eq "constant" ){ encode_sensitive(0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,1);} 
	   if ( $encode eq "average"  ){ encode_sensitive(0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,2,1);}	   
	   if ( $encode eq "variable" ){ encode_sensitive(0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,0,1);}
       }    
   }      
   if ($button_output_ogg->get_active ){
       if ( $check_button_change->get_active){
           if ( $encode eq "average" ) { encode_sensitive(1,0,1,1,0,0,1,0,0,0,0,1,0,1,2,3,1);}
	   if ( $encode eq "variable" ){ encode_sensitive(1,0,0,0,0,0,1,0,0,1,0,1,0,1,2,3,1);}
       }	 
       else{ encode_sensitive(0,0,0,0,0,0,1,0,0,0,0,1,0,0,2,3,1); }    
   }	      
   if ($button_output_mpc->get_active ){
       if ( $check_button_change->get_active){
             encode_sensitive(1,0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0); }	 
       else{ encode_sensitive(0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0); }    
   }
   if ($button_output_ape->get_active ){
       if ( $check_button_change->get_active){
             encode_sensitive(0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,4,2); }	 
       else{ encode_sensitive(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,2); }    
   }
   if ($button_output_flac->get_active ){
       if ( $check_button_change->get_active){
             encode_sensitive(0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,5,2); }	 
       else{ encode_sensitive(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,2); }    
   }
   
   if ($button_output_mp3->get_active){  
      if ( not $check_button_change->get_active){
         if ( $bitrate_average ){ $status_bar->push($context_id, " output: $file_output with average bitrate.");}
         else { $status_bar->push($context_id, " output: $file_output with constant bitrate.");}
      }
      else { $status_bar->push($context_id, " output: $file_output with $encode bitrate.");}	 
   } 
   if ($button_output_ogg->get_active){  
      if ( not $check_button_change->get_active){
         { $status_bar->push($context_id, " output: $file_output with average bitrate.");}
      }
      else { $status_bar->push($context_id, " output: $file_output with $encode bitrate.");}	 
   }  
   if ($button_output_mpc->get_active){  
      $status_bar->push($context_id, " output: $file_output with $encode bitrate."); 
   }
   if ($button_output_ape->get_active){ 
         my $comp_ape = $spinner_compress->get_value_as_int();
         $status_bar->push($context_id, " output: $file_output with compression level = $comp_ape.");
   }
   if ($button_output_flac->get_active){ 
         my $comp_flac = $spinner_compress_flac->get_value();
         $status_bar->push($context_id, " output: $file_output with compression level = $comp_flac.");
   }     
  
}

sub encode_sensitive { # there are 16 widget - the order is important
   my $opt1 = shift; my $opt2 = shift; my $opt3 = shift; my $opt4 = shift;  
   my $opt5 = shift; my $opt6 = shift; my $opt7 = shift; my $opt8 = shift; 
   my $opt9 = shift; my $opt10 = shift; my $opt11 = shift; my $opt12 = shift;
   my $opt13 = shift; my $opt14 = shift; my $opt15 = shift; my $opt16 = shift;
   my $opt17 = shift;
   $combo_const_avg->set_sensitive($opt1);
   $combo_bitrate->set_sensitive($opt2);
   $spinner_bitrate_mp3->set_sensitive($opt3);  
   $spinner_bitrate_ogg->set_sensitive($opt4); 
   $spinner_compress->set_sensitive($opt5);
   $spinner_compress_flac->set_sensitive($opt6);
   $combo_Min->set_sensitive($opt7);
   $combo_mode->set_sensitive($opt8);
   $spinner_V->set_sensitive($opt9);
   $spinner_Vogg->set_sensitive($opt10);
   $spinner_Vmpc->set_sensitive($opt11);
   $combo_Max->set_sensitive($opt12);
   $spinner_q->set_sensitive($opt13);
   $combo_fq->set_sensitive($opt14);
   if ($opt15 == 1){
      $spinner_V->show;
      $spinner_Vogg->hide;
      $spinner_Vmpc->hide;
   }
   if ($opt15 == 2){
      $spinner_V->hide;
      $spinner_Vogg->show;
      $spinner_Vmpc->hide;
   }
   if ($opt15 == 3){
      $spinner_V->hide;
      $spinner_Vogg->hide;
      $spinner_Vmpc->show;
   }
   if ($opt16 == 1){
      $combo_bitrate->show;
      $spinner_bitrate_mp3->hide;
      $spinner_bitrate_ogg->hide;
      $spinner_compress->hide;
      $spinner_compress_flac->hide;
   }
   if ($opt16 == 2){
      $combo_bitrate->hide;
      $spinner_bitrate_mp3->show;
      $spinner_bitrate_ogg->hide;
      $spinner_compress->hide;
      $spinner_compress_flac->hide;
   }
   if ($opt16 == 3){  # ogg 
      $combo_bitrate->hide;
      $spinner_bitrate_mp3->hide;
      $spinner_bitrate_ogg->show;
      $spinner_compress->hide;
      $spinner_compress_flac->hide;
   }
   if ($opt16 == 4){
      $combo_bitrate->hide;
      $spinner_bitrate_mp3->hide;
      $spinner_bitrate_ogg->hide;
      $spinner_compress->show;
      $spinner_compress_flac->hide;
   } 
   if ($opt16 == 5){
      $combo_bitrate->hide;
      $spinner_bitrate_mp3->hide;
      $spinner_bitrate_ogg->hide;
      $spinner_compress->hide;
      $spinner_compress_flac->show;
   } 
   if ($opt17 == 1){
      $label_mp3b->show;
      $label_comp->hide;
   }
   if ($opt17 == 2){
      $label_mp3b->hide;
      $label_comp->show;
   }     
}

#####-------------------final of Combo-------------#####

# criar um label (etiqueta - rtulo) ao lado do menu
$label_mp3b = new Gtk2::Label( "bitrate:" );
$label_mp3b->set_alignment( 1, 0.5 ); 
$table21->attach ($label_mp3b, 4, 5, 0, 1, 'fill', 'shrink', 0, 0);
$label_mp3b->show();

$label_comp = new Gtk2::Label( "comp:" );
$label_comp->set_alignment( 1, 0.5 ); 
$table21->attach ($label_comp, 4, 5, 0, 1, 'fill', 'shrink', 0, 0);
#$label_comp->show();

#####--------------------Combo---------------------#####

$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table21->attach($align,  5, 6, 0, 1, 'fill', 'shrink', 0, 0);
$align->show;

$combo_bitrate = Gtk2::Combo->new;
$combo_bitrate->set_popdown_strings ( 32, 40, 48, 56, 64, 80, 96, 112, 128, 160,  192,  224,  256, 320 );
$combo_bitrate->entry->set_text( $bitrate_constant); #standard value
$combo_bitrate->entry->set_editable($false); 
$combo_bitrate->set_size_request( 68, -1 );
# I think that "changed" is the best signal to use for this choice
$combo_bitrate->entry->signal_connect("changed", \&BitrateChanged, $combo_bitrate);
$align->add($combo_bitrate);
#$combo_bitrate->set_sensitive($false);
#$combo_bitrate->show();

## audio mp3 bitrate is changed
sub BitrateChanged
{
	my ($widget,$comboCopy) = @_;
        my $entry=$comboCopy->entry->get_text();
	if ( $entry ne ""){
	$bitrate_constant = $entry; # copy the value of bitrate changed
	$status_bar->push($context_id, " constant $file_output bitrate set to $bitrate_constant kb/s");
	}
}

#####-------------------final of Combo-------------#####

###--------------------SpinButton--------------------###

# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
$adj = new Gtk2::Adjustment( $bitrate_avr_mp3, 8, 320, 1, 5, 0 );
#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
$spinner_bitrate_mp3 = new Gtk2::SpinButton( $adj, 0.5, 0 );
$spinner_bitrate_mp3->set_wrap( $true );
$spinner_bitrate_mp3->set_size_request( 68, -1 );
$spinner_bitrate_mp3->set_editable($false);
$spinner_bitrate_mp3->set_update_policy( 'if_valid' );
$tooltips->set_tip( $spinner_bitrate_mp3, "Choose a targeted average bitrate. Sets encoding to the bitrate closest to this value (in kb/s).");
$table21->attach($spinner_bitrate_mp3,  5, 6, 0, 1, 'fill', 'shrink', 0, 0);
$spinner_bitrate_mp3->signal_connect("value-changed", sub{ $bitrate_avr_mp3 = $spinner_bitrate_mp3->get_value_as_int(); } );
#$spinner_bitrate_mp3->set_sensitive($false);
$spinner_bitrate_mp3->show();

$adj = new Gtk2::Adjustment( $bitrate_avr_ogg, 8, 320, 1, 5, 0 );
$spinner_bitrate_ogg = new Gtk2::SpinButton( $adj, 0.5, 0 );
$spinner_bitrate_ogg->set_wrap( $true );
$spinner_bitrate_ogg->set_size_request( 68, -1 );
$spinner_bitrate_ogg->set_editable($false);
$spinner_bitrate_ogg->set_update_policy( 'if_valid' );
$tooltips->set_tip( $spinner_bitrate_ogg, "Choose a targeted average bitrate. Sets encoding to the bitrate closest to this value (in kb/s).");
$table21->attach($spinner_bitrate_ogg,  5, 6, 0, 1, 'fill', 'shrink', 0, 0);
$spinner_bitrate_ogg->signal_connect("value-changed", sub{ $bitrate_avr_ogg = $spinner_bitrate_ogg->get_value_as_int(); } );
#$spinner_bitrate_mp3->set_sensitive($false);
#$spinner_bitrate_mp3->show();

# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
$adj = new Gtk2::Adjustment( $comp_ape, 1000, 5000, 1000, 0, 0 );
#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
$spinner_compress = new Gtk2::SpinButton( $adj, 0.1, 0 );
$spinner_compress->set_wrap( $true );
$spinner_compress->set_size_request( 68, -1 );
$spinner_compress->set_editable($false);
$spinner_compress->set_update_policy( 'if_valid' );
$spinner_compress->signal_connect("value-changed", 
sub{ 
   my $comp_ape = $spinner_compress->get_value_as_int();
   $status_bar->push($context_id, " output: $file_output with compression level = $comp_ape.");
} );
$msg = "Compression level:\n  fast: 1000\n  normal: 2000\n  high: 3000\n  extra high: 4000\n  insane: 5000\nSee the help: mac";
$tooltips->set_tip( $spinner_compress, $msg);
$table21->attach($spinner_compress,  5, 6, 0, 1, 'fill', 'shrink', 0, 0);
#$spinner_compress->show();
# $comp = $spinner_compress->get_value_as_int();

# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
$adj = new Gtk2::Adjustment( $comp_flac, 0, 8, 1, 0, 0 );
#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
$spinner_compress_flac = new Gtk2::SpinButton( $adj, 0.1, 0 );
$spinner_compress_flac->set_wrap( $true );
$spinner_compress_flac->set_size_request( 68, -1 );
$spinner_compress_flac->set_editable($false);
$spinner_compress_flac->set_update_policy( 'if_valid' );
$spinner_compress_flac->signal_connect("value-changed", 
sub{ 
   my $comp_flac = $spinner_compress_flac->get_value_as_int();
   $status_bar->push($context_id, " output: $file_output with compression level = $comp_flac.");
} );
$tooltips->set_tip( $spinner_compress_flac, "Compression level:\n  fast: 0\n  normal: 5\n  best: 8\nSee <man flac>.");
$table21->attach($spinner_compress_flac,  5, 6, 0, 1, 'fill', 'shrink', 0, 0);
#$spinner_compress->show();
# $comp = $spinner_compress->get_value_as_int();

###-------------final-of--SpinButton-----------------###

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "kb/s" );
$label->set_alignment( 0.0, 0.5 );   
$table21->attach ($label, 6, 7, 0, 1, 'fill', 'shrink', 0, 0);
$label->show();

#####--------------------Combo---------------------#####
# Min BitRate Quality

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "Min:" ); 
$label->set_alignment( 1, 0.5 );
$table21->attach ($label, 7, 8, 0, 1, 'fill', 'shrink', 0, 0);
$label->show();

$combo_Min = Gtk2::Combo->new;
$combo_Min->set_popdown_strings ( 32, 40, 48, 56, 64, 80, 96, 112, 128, 160,  192,  224,  256, 320 );
$combo_Min->entry->set_text( $vb_Min); #standard value
$combo_Min->set_size_request( 68, -1 );
$combo_Min->entry->set_editable ($false);
$combo_Min->entry->signal_connect("changed", \&bit_Min, $combo_Min);
$table21->attach($combo_Min,  8, 9, 0, 1, 'fill', 'shrink', 0, 0);
$combo_Min->set_sensitive($true);
$combo_Min->show();

## Minimum Bitrate with variable encode
sub bit_Min
{
	my ($widget,$comboCopy) = @_;
        my $entry=$comboCopy->entry->get_text();
	if ( $entry ne ""){
	     $vb_Min = $entry;
	     if ( $vb_Min > $vb_Max ) { # always assert this condition
	          $vb_Min = $vb_Max;
	          $combo_Min->entry->set_text($vb_Min); 
	     }	     
	}
}
#####-------------------final of Combo-------------#####


#####--------------------Combo---------------------#####
# Mode for mp3 files

# criar um label (etiqueta - rtulo) ao lado do menu
my $label_combo_mode = new Gtk2::Label( "Mode:" );  
$label_combo_mode->set_alignment( 1, 0.5 );
$table21->attach ($label_combo_mode, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
$label_combo_mode->show();

# Create a centering alignment object;
$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table21->attach($align,  1, 2, 1, 2, 'fill', 'shrink', 0, 0);
$align->show;

$combo_mode = Gtk2::Combo->new;
$combo_mode->set_popdown_strings ( 'joint-stereo', 'stereo', 'forced joint stereo', 'dual channels', 'mono' );
$combo_mode->entry->set_text( $mode ); #standard value
$combo_mode->set_size_request( 104, -1 );
$combo_mode->entry->set_editable ($false);
$align->add($combo_mode);
#$combo_mode->set_sensitive($false);
$combo_mode->show();

#####-------------------final of Combo-------------#####
#####--------------------Combo---------------------#####
# Variable  BitRate Quality

# criar um label (etiqueta - rtulo) ao lado do menu
$label_VB = new Gtk2::Label( "Variable:" );  
$label_VB->set_alignment( 1, 0.5 );
$table21->attach ($label_VB, 4, 5, 1, 2, 'fill', 'shrink', 0, 0);
$label_VB->show();

###--------------------SpinButton--------------------###

# Create a (left, centering) alignment object;
$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table21->attach($align,  5, 6, 1, 2, 'fill', 'shrink', 0, 0);
$align->show;

$adj = new Gtk2::Adjustment( $V, 0, 9, 1, 0, 0 );
$spinner_V = new Gtk2::SpinButton( $adj, 0.1, 0 );
$spinner_V->set_editable($false);
$spinner_V->set_size_request( 68, -1 );
$spinner_V->set_update_policy( 'if_valid' );
$spinner_V->set_wrap( $false );
my $spinner_V_msg1 = "Adjust the quality of mp3; 9 is the worst quality, 0 is the best quality with more kb/s. See: man lame (option: -V).";
$tooltips->set_tip( $spinner_V, $spinner_V_msg1 );
$align->add($spinner_V);
$spinner_V->set_sensitive($false);
$spinner_V->show();

# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
$adj = new Gtk2::Adjustment( $Vogg, -1.0, 10.0, 0.1, 0, 0 );
$spinner_Vogg = new Gtk2::SpinButton($adj, 0.4, 1);
$spinner_Vogg->set_editable($false);
$spinner_Vogg->set_size_request( 68, -1 );
$spinner_Vogg->set_update_policy( 'if_valid' );
$spinner_Vogg->set_wrap( $false );
$msg =  "Adjust the quality of encode for ogg; 10 is the best quality with more kb/s used,";
$msg .= " -1 is the worst quality. See: man oggenc.";
$tooltips->set_tip( $spinner_Vogg, $msg );
$table21->attach($spinner_Vogg,  5, 6, 1, 2, 'fill', 'shrink', 0, 0);
#$spinner_Vogg->set_sensitive($false);
#$spinner_Vogg->show();

# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
$adj = new Gtk2::Adjustment( $Vmpc, 0.0, 10.0, 0.1, 0, 0 );
$spinner_Vmpc = new Gtk2::SpinButton($adj, 0.4, 1);
$spinner_Vmpc->set_editable($false);
$spinner_Vmpc->set_size_request( 68, -1 );
$spinner_Vmpc->set_update_policy( 'if_valid' );
$spinner_Vmpc->set_wrap( $false );
$msg =  "Adjust the quality of encode for mpc; 10 is the best quality with more kb/s used,";
$msg .= " 0 is the worst quality. See: mppenc.\n";
$msg .= " q=2 --telephone - lowest quality,       (32... 48 kbps)\n";
$msg .= " q=3 --thumb     - low quality/internet, (58... 86 kbps)\n";
$msg .= " q=4 --radio     - medium (MP3) quality, (112...152 kbps)\n";
$msg .= " q=5 --standard  - high quality (dflt),  (142...184 kbps)\n";
$msg .= " q=6 --xtreme    - extreme high quality, (168...212 kbps)\n";
$msg .= " q=7 --insane    - extreme high quality, (232...268 kbps)\n";
$msg .= " q=8 --braindead - extreme high quality, (232...278 kbps)";
$tooltips->set_tip( $spinner_Vmpc, $msg );
$table21->attach($spinner_Vmpc,  5, 6, 1, 2, 'fill', 'shrink', 0, 0);
#$spinner_Vogg->set_sensitive($false);
#$spinner_Vogg->show();       

###-------------final-of--SpinButton-----------------###
#####--------------------Combo---------------------#####
# Max BitRate Quality

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "Max:" );   
$label->set_alignment( 1, 0.5 );
$table21->attach ($label, 7, 8, 1, 2, 'fill', 'shrink', 0, 0);
$label->show();

$combo_Max = Gtk2::Combo->new;
$combo_Max->set_popdown_strings ( 32, 40, 48, 56, 64, 80, 96, 112, 128, 160,  192,  224,  256, 320 );
$combo_Max->entry->set_text($vb_Max); #standard value
$combo_Max->set_size_request( 68, -1 );
$combo_Max->entry->set_editable ($false);
$combo_Max->entry->signal_connect("changed", \&bit_Max, $combo_Max);
$table21->attach($combo_Max,  8, 9, 1, 2, 'fill', 'shrink', 0, 0);
$combo_Max->set_sensitive($true);
$combo_Max->show();

## Maximum Bitrate with variable encode
sub bit_Max
{
	my ($widget,$comboCopy) = @_;
        my $entry=$comboCopy->entry->get_text();
	if ( $entry ne ""){
	     $vb_Max = $entry;
	     if ( $vb_Max < $vb_Min ) { # always assert this condition
	          $vb_Max = $vb_Min;
	          $combo_Max->entry->set_text($vb_Max); 
	     }
	}
}
#####-------------------final of Combo-------------#####

#####-------------------final of Combo-------------#####

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "encode quality:" );
$label->set_alignment( 1.0, 0.5 );
$table21->attach ($label, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
$label->show();

###--------------------SpinButton--------------------###

# Create a (left, centering) alignment object;
$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table21->attach($align,  1, 2, 2, 3, 'fill', 'shrink', 0, 0);
$align->show;

# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
$adj = new Gtk2::Adjustment( $quality, 0, 9, 1, 5, 0 );
#$spin = new Gtk2::SpinButton( $adjustment, $climb_rate, $digits );
$spinner_q = new Gtk2::SpinButton( $adj, 0, 0 );
$spinner_q->set_wrap( $true );
$spinner_q->set_editable($false);
$spinner_q->set_size_request( 50, -1 );
$spinner_q->set_update_policy( 'if_valid' );
my $spinner_q_msg = "Adjust the quality of encode for mp3 files: 0 is the best quality with maximum ";
$spinner_q_msg .= "algorithms process, 9 is the worst quality but more speed. See: man lame (option: -q).";
$tooltips->set_tip( $spinner_q, $spinner_q_msg );
$align->add($spinner_q);
$spinner_q->signal_connect("value-changed", sub{ $quality = $spinner_q->get_value; } );
$spinner_q->show();

###-------------final-of--SpinButton-----------------###

#####--------------------Combo---------------------#####
# Frequency

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "Freq:" );   
$label->set_alignment( 1, 0.5 );
$table21->attach ($label, 4, 5, 2, 3, 'fill', 'shrink', 0, 0);
$label->show();

$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$table21->attach($align, 5, 6, 2, 3, 'fill', 'shrink', 0, 0);
$align->show;

$combo_fq = Gtk2::Combo->new;
$combo_fq->set_popdown_strings ( 8.0, 11.025, 12.0, 16.0, 22.05, 24.0, 32.0, 44.1, 48.0 );
$combo_fq->entry->set_text($freq); #standard value 
$combo_fq->set_size_request( 68, -1 );
$combo_fq->entry->set_editable ($false);
$align->add($combo_fq);
$combo_fq->set_sensitive($change_properties);
$combo_fq->show();

$label = new Gtk2::Label( "KHz" );
$label->set_alignment( 0.0, 0.5 );  
$table21->attach ($label, 6, 7, 2, 3, 'fill', 'shrink', 0, 0);
$label->show();

#####-------------------final of Combo-------------#####


###-----------------------------------------------------###
#####---------------- T a b l e 3 --------------------#####

###----------------id3 tag & mpeg info---------------###

$label = new Gtk2::Label( "Title:" );
$label->set_alignment( 1.0, 0.5 );   
$table3->attach ($label, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
$label->show();

# Create the Entry to put the title of mp3 music
my $entry_title = new Gtk2::Entry();
$entry_title->set_text( "" );
#$entry_title->set_size_request(260);
$entry_title->signal_connect("changed",\&change_tag);
$table3->attach_defaults ($entry_title, 1, 6, 0, 1 );
$entry_title->show();

$label = new Gtk2::Label( "Artist:" );
$label->set_alignment( 1.0, 0.5 );   
$table3->attach ($label, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
$label->show();

# Create the Entry to put the artist of mp3 music
my $entry_artist = new Gtk2::Entry();
$entry_artist->set_text( "" );
$entry_artist->signal_connect("changed",\&change_tag);
$table3->attach_defaults ($entry_artist, 1, 6, 1, 2 );
$entry_artist->show();

$label = new Gtk2::Label( "Album:" );
$label->set_alignment( 1.0, 0.5 );   
$table3->attach ($label, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
$label->show();

# Create the Entry to put the album of mp3 music
my $entry_album = new Gtk2::Entry();
$entry_album->set_text( "" );
$entry_album->signal_connect("changed",\&change_tag);
$table3->attach_defaults ($entry_album, 1, 6, 2, 3 );
$entry_album->show();

$label = new Gtk2::Label( "Comment:" );
$label->set_alignment( 1.0, 0.5 ); 
$table3->attach ($label, 0, 1, 3, 4, 'fill', 'shrink', 0, 0);
$label->show();

# Create the Entry to put the comment of mp3 music
my $entry_comment = new Gtk2::Entry();
$entry_comment->set_text( "" );
$entry_comment->signal_connect("changed",\&change_tag);
$table3->attach_defaults ($entry_comment, 1, 6, 3, 4 );
$entry_comment->show();

$label = new Gtk2::Label( "Year:" );
$label->set_alignment( 1.0, 0.5 );   
$table3->attach ($label, 0, 1, 4, 5, 'fill', 'shrink', 0, 0);
$label->show();

my $entry_year = new Gtk2::Entry();
$entry_year->set_text( "" );
$entry_year->set_size_request(50);
$entry_year->signal_connect("changed",\&change_tag);
$table3->attach_defaults ($entry_year, 1, 2, 4, 5 );
$entry_year->show();

$label = new Gtk2::Label( "Track Number:" );
$label->set_alignment( 1.0, 0.5 );   
$table3->attach ($label, 2, 3, 4, 5, 'fill', 'shrink', 0, 0);
$label->show();

# Create the Entry to put the track number of mp3 music
my $entry_tn = new Gtk2::Entry();
$entry_tn->set_text( "" );
$entry_tn->set_size_request(40);
$tooltips->set_tip( $entry_tn, "Track number");
$entry_tn->signal_connect("changed",\&change_tag);
$table3->attach_defaults ($entry_tn, 3, 4, 4, 5 );
$entry_tn->show();

$label = new Gtk2::Label( "/" );
$label->set_alignment( 1.0, 0.5 );   
$table3->attach ($label, 4, 5, 4, 5, 'fill', 'shrink', 0, 0);
$label->show();

my $entry_tt = new Gtk2::Entry();
$entry_tt->set_text( "" );
$entry_tt->set_size_request(40);
$tooltips->set_tip( $entry_tt, "Total track number");
$entry_tt->signal_connect("changed",\&change_tag);
$table3->attach_defaults ($entry_tt, 5, 6, 4, 5 );
$entry_tt->show();

#####----------------Combo---mp3 info--------------#####
# Genre of mp3 info - see  mp3info  -G

# criar um label (etiqueta - rtulo) ao lado do menu
$label = new Gtk2::Label( "Genre:" );
$label->set_alignment( 1.0, 0.5 );  
$table3->attach ($label, 0, 1, 5, 6, 'fill', 'shrink', 0, 0);
$label->show();

# my $cmd = "mp3info -G";
# my $cmd = "lame --genre-list"; # see the command: < lame --genre-list >
# my $list_genre = exec_cmd_system($cmd); # list with all mp3 genre
# $list_genre =~ s/\s*(\d+)\s(.*)/ $1 => "$2", /g;  
# print "$list_genre";

# A hash represents a set of key/value pairs:  --- see perlintro, perlref, perlreftut
# 147 genre are default;; more than 148 is personal ;;  max number = 255
my %genre_hash = (  123 => "A Cappella",  34 => "Acid",  74 => "Acid Jazz",  73 => "Acid Punk",  99 => "Acoustic",  
20 => "Alternative",  40 => "Alt. Rock",  26 => "Ambient",  145 => "Anime",  90 => "Avantgarde",  116 => "Ballad",  
41 => "Bass",  135 => "Beat",  85 => "Bebob",  96 => "Big Band",  138 => "Black Metal",  89 => "Bluegrass",  0 => "Blues",  
107 => "Booty Bass",  132 => "BritPop",  65 => "Cabaret",  88 => "Celtic",  104 => "Chamber Music",  102 => "Chanson",  
97 => "Chorus",  136 => "Christian Gangsta Rap",  61 => "Christian Rap",  141 => "Christian Rock",  32 => "Classical",  
1 => "Classic Rock",  112 => "Club",  128 => "Club-House",  57 => "Comedy",  140 => "Contemporary Christian",  2 => "Country",  
139 => "Crossover",  58 => "Cult",  3 => "Dance",  125 => "Dance Hall",  50 => "Darkwave",  22 => "Death Metal",  4 => "Disco",  
55 => "Dream",  127 => "Drum & Bass",  122 => "Drum Solo",  120 => "Duet",  98 => "Easy Listening",  52 => "Electronic",  
48 => "Ethnic",  54 => "Eurodance",  124 => "Euro-House",  25 => "Euro-Techno",  84 => "Fast-Fusion",  80 => "Folk",  
115 => "Folklore",  81 => "Folk/Rock",  119 => "Freestyle",  5 => "Funk",  30 => "Fusion",  36 => "Game",  
59 => "Gangsta Rap",  126 => "Goa",  38 => "Gospel",  49 => "Gothic",  91 => "Gothic Rock",  6 => "Grunge",  
129 => "Hardcore",  79 => "Hard Rock",  137 => "Heavy Metal",  7 => "Hip-Hop",  35 => "House",  100 => "Humour",  
131 => "Indie",  19 => "Industrial",  33 => "Instrumental",  46 => "Instrumental Pop",  47 => "Instrumental Rock",  
8 => "Jazz",  29 => "Jazz+Funk",  146 => "JPop",  63 => "Jungle",  86 => "Latin",  71 => "Lo-Fi",  45 => "Meditative",  
142 => "Merengue",  9 => "Metal",  77 => "Musical",  82 => "National Folk",  64 => "Native American",  133 => "Negerpunk",  
10 => "New Age",  66 => "New Wave",  39 => "Noise",  11 => "Oldies",  103 => "Opera",  12 => "Other",  75 => "Polka",  
134 => "Polsk Punk",  13 => "Pop",  53 => "Pop-Folk",  62 => "Pop/Funk",  109 => "Porn Groove",  117 => "Power Ballad",  
23 => "Pranks",  108 => "Primus",  92 => "Progressive Rock",  67 => "Psychedelic",  93 => "Psychedelic Rock",  43 => "Punk",  
121 => "Punk Rock",  15 => "Rap",  68 => "Rave",  14 => "R&B",  16 => "Reggae",  76 => "Retro",  87 => "Revival",  
118 => "Rhythmic Soul",  17 => "Rock",  78 => "Rock & Roll",  143 => "Salsa",  114 => "Samba",  110 => "Satire",  
69 => "Showtunes",  21 => "Ska",  111 => "Slow Jam",  95 => "Slow Rock",  105 => "Sonata",  42 => "Soul",  37 => "Sound Clip",  
24 => "Soundtrack",  56 => "Southern Rock",  44 => "Space",  101 => "Speech",  83 => "Swing",  94 => "Symphonic Rock",  
106 => "Symphony",  147 => "Synthpop",  113 => "Tango",  18 => "Techno",  51 => "Techno-Industrial",  130 => "Terror",  
144 => "Thrash Metal",  60 => "Top 40",  70 => "Trailer",  31 => "Trance",  72 => "Tribal",  27 => "Trip-Hop",  28 => "Vocal",
148 => "MPB", 149 => "Bossa Nova" );

# print "\nhash of genre --- element 32: ",$genre_hash{"32"}," \n";

my @array_genre = (); # array with only the names 
for my $key (0..149) {
   #print "$key => $genre_hash{$key}\n";
   push @array_genre,$genre_hash{$key};
}

my $combo_genre = Gtk2::Combo->new;
$combo_genre->set_popdown_strings ( sort @array_genre );  # sort the array
$combo_genre->entry->set_text( ' ' ); #standard value
#$combo->set_size_request( 70, -1 );
$combo_genre->entry->set_editable ($true);
$combo_genre->entry->signal_connect("changed", \&change_tag);
$table3->attach($combo_genre, 1, 6, 5, 6, 'fill', 'fill', 0, 0);
$combo_genre->show();


# when whatever tag is changed, the button_save_tag is set to set_sensitive($true)
sub change_tag {
      my $title=$entry_title->get_text;
      my $artist=$entry_artist->get_text;
      my $album=$entry_album->get_text;
      my $comment=$entry_comment->get_text;
      my $year=$entry_year->get_text;             
      my $tn = ""; 
      if ( $entry_tn->get_text =~ /(\d+)/ ) { $tn = $1; } 
      my $tt = ""; 
      if ( $entry_tt->get_text =~ /(\d+)/ ) { $tt = $1; }
         
      my $genre=$combo_genre->entry->get_text;
      
      # print "(tn,mp3_tn) = ($tn,$mp3_tn) ;; (tt,total_track) = ($tt,$total_track)\n";

      # always in this order - there are 07 entries at all.
      # @array_info_from_entry = ($title,$artist,$album,$comment,$year,$tn,$genre);		    
      
      # verify any change in mp3 tag
      if ( ($file_extension =~ /(mp3|ogg|flac|mpc|ape)/) and ($title ne $mp3_title or 
          $artist ne $mp3_artist or $album ne $mp3_album  or $comment ne $mp3_comment or 
	  $year ne $mp3_year or $tn ne $mp3_tn or $tt ne $total_track or $genre ne $mp3_genre) )
      {    
          if ($tt ne "" and $tn ne "") { 
	     if ($tt < $tn){ #only compare ($tt < $tn) if (tt ne "")
	        $button_save_tag->set_sensitive($false);
	        return ($title,$artist,$album,$comment,$year,$tn,"",$genre);
	     } 
	  }  
	  if ($tn eq "" and $tt ne "") { 
	     $button_save_tag->set_sensitive($false);
	     return ($title,$artist,$album,$comment,$year,$tn,"",$genre); 
	  }
          $button_save_tag->set_sensitive($true);
	  $tooltips->set_tip( $button_save_tag, "Save changes of tag to $file_extension file.");
	  $status_bar->push($context_id, ""); # erase status_bar
      }	 
      else { $button_save_tag->set_sensitive($false);}
      
      if ( $file_extension =~ /(wav|cda)/ ){ # for wav/ape/cda file
	  # get info to be saved on normalized file
          $status_bar->push($context_id, " This informations will be saved on normalized $file_output file.");
      }
      return ($title,$artist,$album,$comment,$year,$tn,$tt,$genre);
}

#####-------------final of Combo---mp3 info--------#####


###----------------------table 31 -------------------###

my $label_mpeg = new Gtk2::Label( "\nMPEG" );
#$label_mpeg->set_size_request( 152,-1 );  # set the size
#$label_mpeg->set_justify( 'left' );   
$label_mpeg->set_alignment( 0, 0.5 );
$table31->attach ($label_mpeg, 0, 1, 1, 2, 'fill', 'shrink', 0, 0 );
$label_mpeg->show();
#$label_mpeg->set_label ($str);

my $label_kbps = new Gtk2::Label( "Bitrate:" );
#$label_kbps->set_size_request( 152,-1 );  # set the size  
$label_kbps->set_alignment( 0, 0.5 );
$table31->attach ($label_kbps, 0, 1, 2, 3, 'fill', 'shrink', 0, 0 );
$label_kbps->show();

my $label_freq = new Gtk2::Label( "Frequency:" );  
$label_freq->set_alignment( 0, 0.5 );
$table31->attach ($label_freq, 0, 1, 3, 4, 'fill', 'shrink', 0, 0);
$label_freq->show();

my $label_mode = new Gtk2::Label( "Mode:" ); 
$label_mode->set_alignment( 0, 0.5 );   
$table31->attach ($label_mode, 0, 1, 4, 5, 'fill', 'shrink', 0, 0);
$label_mode->show();

my $label_time = new Gtk2::Label( "Time:" );  
$label_time->set_alignment( 0, 0.5 ); 
$table31->attach ($label_time, 0, 1, 5, 6, 'fill', 'shrink', 0, 0);
$label_time->show();

$separator = Gtk2::HSeparator->new;
$table31->attach_defaults ( $separator, 0, 1, 6, 7 );
$separator->show;

$button_save_tag = Gtk2::Button->new_from_stock ('gtk-save');
#$tooltips->set_tip( $button_save_tag, "Save changes of tag to $file_extension file.");
$button_save_tag->signal_connect( "clicked", \&save_tag );
$table31->attach ($button_save_tag,  0, 1, 7, 8 , 'shrink', 'shrink', 0, 0);
$button_save_tag->set_sensitive($false);
$button_save_tag->set( 'focus-on-click' => $false, 'relief' => 'none' );	 
$button_save_tag->show();


###----------------id3 tag & mpeg info---------------###
######--------------------------------------------######

#--------------------- I c o n s ----------------------#

my @items = ( { stock_id => "gtk-apply", label => "_unselect" },
              { stock_id => "gtk-go-down", label => "_select" },
	      { stock_id => "gtk-go-up", label => "Debug" },
	      { stock_id => "gtk-cdrom", label => ""      },
	      { stock_id => "gtk-close", label => ""      },
	      { stock_id => "gtk-cancel", label => ""     },
	      { stock_id => "gtk-refresh", label => ""    },
	      { stock_id => "gtk-go-back", label => ""    },
	      { stock_id => "gtk-go-forward", label => "" },
	      { stock_id => "gtk-goto-first", label => "" },
	      { stock_id => "gtk-goto-last", label => ""  },
              { stock_id => "gtk-yes", label => "_normalize", } );
Gtk2::Stock->add (@items);  # Register our stock items

###--------------------------------------------------###

###----------------------table 4 --------------------###
#--------------------------Rip-------------------------#

# some buttons
my $hbox4 = Gtk2::HBox->new ($false, 2);
$vbox4->pack_start ($hbox4, $false, $false, 0);
$hbox4->show;

$label = new Gtk2::Label( " cdrom:" );
$label->set_alignment( 1.0, 0.5 );   
$hbox4->pack_start ($label, $false, $true, 0);
$label->show();

# Create the Entry to put the track number of mp3 music
$entry_cda = new Gtk2::Entry();
$entry_cda->set_text($audiodevide_path);
$tooltips->set_tip( $entry_cda, "Try /dev/dvd or /dev/hdc ...");
$entry_cda->set_size_request(120);
$entry_cda->signal_connect("changed", \&entry_cda_change );
$hbox4->pack_start ($entry_cda, $false, $false, 0);
$entry_cda->show();

$button = Gtk2::Button->new_from_stock ('gtk-cdrom');
$button->set_size_request( 40, -1 );
$button->set( 'focus-on-click' => $false, 'relief' => 'none' );
$tooltips->set_tip( $button, "refresh cdrom drive" );
$button->signal_connect (clicked => \&refresh_cda);
$hbox4->pack_start ($button, $false, $false, 0);
$button->show;

# my $button_selec = Gtk2::Button->new ("unselect all");
my $button_selec = Gtk2::Button->new_from_stock ('gtk-close');
$button_selec->set_size_request( 40, -1 );
$button_selec->set( 'focus-on-click' => $false, 'relief' => 'none' );
$tooltips->set_tip( $button_selec, "select all tracks" );
$button_selec->signal_connect (clicked => \&unselect_cda);
#$button_selec->set_sensitive($false);
$button_selec->show;

my $button_unselec = Gtk2::Button->new_from_stock ('gtk-cancel');
$button_unselec->set_size_request( 40, -1 );
$button_unselec->set( 'focus-on-click' => $false, 'relief' => 'none' );
$tooltips->set_tip( $button_unselec, "unselect all tracks" );
$button_unselec->signal_connect (clicked => \&unselect_cda);
$button_unselec->set_sensitive($false);
$button_unselec->show;

# this align is a container that hold two buttons alternately
my $align_selec = Gtk2::Alignment->new(0.5, 0.5 , 0, 0);
$align_selec->add ($button_unselec);
$hbox4->pack_start ($align_selec, $false, $false, 0);
$align_selec->show;

# to fill the empty space
$label = new Gtk2::Label( " " );
$label->set_alignment( 0.5, 0.5 );   
$hbox4->pack_start ($label, $true, $true, 0);
$label->show();


my $button_go_back = Gtk2::Button->new_from_stock ("gtk-go-back");
$button_go_back->set( 'focus-on-click' => $false, 'relief' => 'none' );
$button_go_back->set_size_request( 34, -1 );
$button_go_back->signal_connect ('clicked', sub { 
   if ( $playing_audio_cd eq $true ){ $go_back = $true; }
   else { $go_back = $false; }
} );
$hbox4->pack_start ($button_go_back, $false, $false, 0);
$button_go_back->set_sensitive($false);
$button_go_back->show;


#------------------------ display -----------------------#

# convert displayLeft.png -resize 12x44 -enhance -colors 16 xpm_dleft.xpm 
# convert xpm_dleft.xpm -resample 12x40 -colors 16 -enhance  xpm_dleft2.xpm
# cat xpm_dleft2.xpm | sed "s/\\\$/S/g" | sed "s/\@/A/g"|sed "/pixels/d" >xpm_dleft3.xpm
# 12 - 40
my @xpm_dleft = (  #not used
# /* columns rows colors chars-per-pixel */
"12 40 16 1",
"  c #84867B",
". c #919289",
"X c #ABAD9E",
"o c #E6EDBD",
"O c #E6EDBD",
"+ c #E2E9B9",
"A c #E8EFBE",
"# c #E9F0BF",
"S c #CCD2A7",
"% c #EBF2C1",
"& c #E6E7E6",
"* c #E9EAE9",
"= c #F1F3F1",
"- c #CCCDCF",
"; c #BFBFC2",
": c #575850",
"&&&&&&&&&=; ",
"&&&&&&&&*X.+",
"&&&&&&&&.X%O",
"&&&&&&& S%OO",
"&&&&&& S%OOO",
"&&&**.S%OOoo",
"&&**XX%OOooo",
"&&*- %OOoooo",
"&**.+OOOoooo",
"&*;X%Ooooooo",
"**.+OOoooooo",
"*;.%OOoooooo",
"*.SAOooooooo",
"*.#OOooooooo",
";.%OOooooooo",
"XX%OOooooooo",
".S#OOooooooo",
":+OOoooooooo",
":ooooooooooo",
":Ooooooooooo",
":Ooooooooooo",
":OOooooooooo",
":oOOoooooooo",
".SAOOooooooo",
"XS%OOooooooo",
";X%Ooooooooo",
"&.#Ooooooooo",
"=.+AOooooooo",
"=XX%OOoooooo",
"*&.#OOoooooo",
"&*XX#OOooooo",
"&&&.#AOooooo",
"&&*XX%Oooooo",
"&&&*.SAOoooo",
"&&&&& oOOooo",
"&&&&*- OOOoo",
"&&&&&*- OAOO",
"&&&&&**- S%A",
"&&&&&&&*&.X%",
"&&&&&&&&**X ");


my @xpm = (  # not used
'32 32 17 1',
' 	c None',
'.	c #030303',
'+	c #1A1A1A',
'@	c #4A4A4A',
'#	c #616161',
'$	c #939393',
'%	c #AAAAAA',
'&	c #D2D2D2',
'*	c #DCDCDC',
'=	c #E2E2E2',
'-	c #E6E6E6',
';	c #EDEDED',
'>	c #343434',
',	c #AEAEAE',
'x	c #7B7B7B',
')	c #C5C5C5',
'!	c #F3F3F3',
'                                ',
'                                ',
'                                ',
'            )&&&&)),            ',
'          )=*=--=-=;*&          ',
'         )*-;;!;;!;;;**         ',
'        )=;!)x@>+>#)!;!*        ',
'       &*!;#.>x%%x..>&!!&       ',
'       =;!@.$!!;;!=+.+;;=&      ',
'      );!%..;!;--;!$..x!;&      ',
'      *;;#..&!-&*;!&..>!;&      ',
'      &=;#..x!=&*;!&..>!;&      ',
'      )*;*@>&;&)=;!$..@!;&      ',
'       &*;;;;=&=;!!+..,!;*      ',
'        &--***=;!*@..#!;=       ',
'         )&)&=;!$..+x!;=&       ',
'           %&;!#.@%;!!=&        ',
'           &&;$.x!!;;-*         ',
'           &-!#.!!;=&)          ',
'           )-!@@;;*)            ',
'           )*;x$;-)             ',
'           &;!!!;;*             ',
'          &*!&@@);=&            ',
'          *=;@..@;=)            ',
'          *-;@..>!;*            ',
'          )-;)>>&-*&            ',
'           )=!!;;=)             ',
'            &=-;=)              ',
'             )**)               ',
'                                ',
'                                ',
'                                ');


my ($drag, $mask) = Gtk2::Gdk::Pixmap->colormap_create_from_xpm_d (
	undef, $window->get_colormap(), undef, @xpm);
my $pixmap = Gtk2::Image->new_from_pixmap ($drag, $mask);

my $pixbuf_left = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@xpm_dleft);

# see /usr/share/doc/perl-Gtk2-1.054/gtk-demo/drawingarea.pl
# GtkDrawingArea is a blank area where you can draw custom displays
# of various kinds.

my $da = Gtk2::DrawingArea->new;
# set a minimum size
$da->set_size_request (140, 30); 
$hbox4->pack_start ($da, $false, $false, 0);
$da->signal_connect (expose_event => \&expose_event);
$da->signal_connect (expose_event => \&draw_layout,"00:00/00:00");
$da->signal_connect (expose_event => \&draw_random);
$da->signal_connect (button_press_event => \&button_press_event);
$da->set_events ([ @{ $da->get_events },
##                         'exposure-mask',
                         'leave-notify-mask',
                         'button-press-mask' ]);
$da->set_sensitive($false);		 
$da->show;
 
# Redraw the screen from the pixmap
sub expose_event {
  my ($widget, $event, $data) = @_;       # $widget = $da = Gtk2::DrawingArea
                                          # $drawable = $widget->state;  Gtk2::Gdk::Drawable
   
   my $height = $widget->allocation->height;  # height of $widget $da
   my $width = $widget->allocation->width ;
   
   my $gc5 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color5 = Gtk2::Gdk::Color->new (230*256, 237*256, 189*256); # #E6EDBD
   $gc5->set_rgb_fg_color ($color5);
   
   my $corner = 4;	
   my $height_ty = 0.35*$corner;	
   my $width_tx = 0.35*$corner;		   
   # $drawable->draw_polygon ($gc, $filled, $x1, $y1, ...)
   $widget->window->draw_polygon ($gc5, $true, # fill whith color $gc5 
                                      $corner, 0, $width - $corner, 0,
				      $width - $width_tx, $height_ty,
                                      $width, $corner, $width, $height - $corner,
				      $width - $width_tx, $height - $height_ty,
				      $width - $corner, $height, $corner, $height,
				      $width_tx, $height - $height_ty,
				      0, $height - $corner, 0, $corner,
				      $width_tx, $height_ty );
				      
   my $gc4 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color4 = Gtk2::Gdk::Color->new (160*256, 184*256, 163*256); # #A0B8A3 = 160 184 163
   $gc4->set_rgb_fg_color ($color4);
   				      
   # draw the limite lines
   # $drawable->draw_polygon ($gc, $filled, $x1, $y1, ...)
   $widget->window->draw_polygon ($gc4, $false, # $widget->style->black_gc,
                                      $corner, 0, $width - $corner - 1, 0,
				      $width - $width_tx, $height_ty,
                                      $width - 1, $corner, $width - 1, $height - $corner - 1,
				      $width - $width_tx, $height - $height_ty,
				      $width - $corner - 1, $height - 1, $corner, $height - 1,
				      $width_tx, $height - $height_ty,
				      0, $height - $corner -1 , 0, $corner, 
				      $width_tx, $height_ty );				      
				      	
				         
   my $gc1 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color = Gtk2::Gdk::Color->new (85*256, 90*256, 190*256); #RGB = 85 90 190
   $gc1->set_rgb_fg_color ($color);   
   my $gc2 = Gtk2::Gdk::GC->new ($widget->window);
   my $color2 = Gtk2::Gdk::Color->new (230*256, 237*256, 189*256); # #E6EDBD
   $gc2->set_rgb_fg_color ($color2);
   my $purple = Gtk2::Gdk::Color->parse ('purple');
   my $color3 = Gtk2::Gdk::Color->parse ('#E6EDBD');
   my $red = Gtk2::Gdk::Color->parse ('red');
   
   my $dcenterx = 0;  # distance of rectangle from center of $widget
   my $delta = 5;     # distance inter buttons
   my $width_r = 8; 
   my $height_r = 8;  # height of buttons
    
   # stop button   
   $widget->window->draw_rectangle ($gc1, $true, $width/2 - $width_r/2, $height - $height_r - 2, $width_r, $height_r); 	
   
   # play button
   # $drawable->draw_polygon ($gc, $filled, $x1, $y1, ...)
   $widget->window->draw_polygon ($gc1, $true, $width/2 + $width_r/2 + $delta, $height - 2, $width/2 + $width_r/2 + $delta, $height - $height_r - 2,
                                               $width/2 + 3*$width_r/2 + $delta, $height - $height_r/2 - 2 );				    				    		      
   # pause lines
   $widget->window->draw_lines ($gc1, $width/2 + 3*$width_r/2 + 2*$delta + 0, $height - 3, $width/2 + 3*$width_r/2 + 2*$delta + 0, $height - $height_r - 2 );
   $widget->window->draw_lines ($gc1, $width/2 + 3*$width_r/2 + 2*$delta + 1, $height - 3, $width/2 + 3*$width_r/2 + 2*$delta + 1, $height - $height_r - 2 );
   $widget->window->draw_lines ($gc1, $width/2 + 3*$width_r/2 + 2*$delta + 2, $height - 3, $width/2 + 3*$width_r/2 + 2*$delta + 2, $height - $height_r - 2 );   
   
   $widget->window->draw_lines ($gc1, $width/2 + 3*$width_r/2 + 2*$delta + 1+$width_r/2, $height - 3, $width/2 + 3*$width_r/2 + 2*$delta + 1+$width_r/2, $height - $height_r - 2 );
   $widget->window->draw_lines ($gc1, $width/2 + 3*$width_r/2 + 2*$delta + 2+$width_r/2, $height - 3, $width/2 + 3*$width_r/2 + 2*$delta + 2+$width_r/2, $height - $height_r - 2 );
   $widget->window->draw_lines ($gc1, $width/2 + 3*$width_r/2 + 2*$delta + 3+$width_r/2, $height - 3, $width/2 + 3*$width_r/2 + 2*$delta + 3+$width_r/2, $height - $height_r - 2 );   
   
   # fastforward button of width = 2*$width_r - $width_r/4 = 7*$width_r/4
   # $drawable->draw_polygon ($gc, $filled, $x1, $y1, ...)
   $widget->window->draw_polygon ($gc1, $true, $width/2 + 5*$width_r/2 + 3*$delta, $height - 2, 
                                               $width/2 + 5*$width_r/2 + 3*$delta, $height - $height_r - 2,
                                               $width/2 + 7*$width_r/2 + 3*$delta, $height - $height_r/2 - 2 );	
   $widget->window->draw_polygon ($gc1, $true, $width/2 + 7*$width_r/2 + 3*$delta - $width_r/4, $height - 2, 
                                               $width/2 + 7*$width_r/2 + 3*$delta - $width_r/4, $height - $height_r - 2,
                                               $width/2 + 9*$width_r/2 + 3*$delta - $width_r/4, $height - $height_r/2 - 2 );	
   
   # rewind button
   # $drawable->draw_polygon ($gc, $filled, $x1, $y1, ...)
   $widget->window->draw_polygon ($gc1, $true, $width/2 - 1*$width_r/2 - 1*$delta, $height - 2, 
                                               $width/2 - 1*$width_r/2 - 1*$delta, $height - $height_r - 2,
                                               $width/2 - 3*$width_r/2 - 1*$delta, $height - $height_r/2 - 2 );	
   $widget->window->draw_polygon ($gc1, $true, $width/2 - 3*$width_r/2 - 1*$delta + $width_r/4, $height - 2, 
                                               $width/2 - 3*$width_r/2 - 1*$delta + $width_r/4, $height - $height_r - 2,
                                               $width/2 - 5*$width_r/2 - 1*$delta + $width_r/4, $height - $height_r/2 - 2 );	
					       
   # eject button     5/2 + 7/4 = 17/4
   $widget->window->draw_lines ($gc1, $width/2 + 17*$width_r/4 + 4*$delta, $height - 3, $width/2 + 21*$width_r/4 + 4*$delta -1, $height - 3 );
   $widget->window->draw_lines ($gc1, $width/2 + 17*$width_r/4 + 4*$delta, $height - 4, $width/2 + 21*$width_r/4 + 4*$delta -1, $height - 4 );
   $widget->window->draw_polygon ($gc1, $true, $width/2 + 17*$width_r/4 + 4*$delta - 1, $height - 5, 
                                               $width/2 + 21*$width_r/4 + 4*$delta + 1, $height - 5,
				               $width/2 + 19*$width_r/4 + 4*$delta + 0, $height - $height_r - 2 );
     
   # $drawable->draw_pixbuf ($gc, $pixbuf, $src_x, $src_y, $dest_x, $dest_y, $width, $height, $dither, $x_dither, $y_dither)
   # $widget->window->draw_pixbuf ($gc1, $pixbuf_left, 0, 5, 0, 0, 12, 30, 'normal', 0, 0);
   # $widget->window->draw_pixbuf ($gc1, $pixbuf_right, 0, 5, 88, 0, 12, 30, 'normal', 0, 0);
   
   #my $pi = 22800 ; # 22800 = 360 degrees ??
   #$widget->window->draw_arc ($gc2, $true, 0, 0, $height, $height, $pi/4, $pi/2 ); 
   
   # rand and loop layouts
   draw_random($da,undef,("rand", $corner/2 + 2, $corner/2));
   draw_random($da,undef,("loop", $corner/2 + 2, $height - $corner/2));
        
  return $false;
}

# Draw a layout display $min:$sec on the screen for cdplayer
# see <man Gtk2::Gdk::Drawable>
my $width_lay; my $height_lay; 
my $enter_lay = $false; my $size; # to determine the font size

sub draw_layout {
   my ($widget, $event, $data) = @_;   # $widget = $da = Gtk2::DrawingArea

   my $height = $widget->allocation->height;  # height of $widget $da
   my $width = $widget->allocation->width ;
   my $corner = 4;
				          
   my $gc1 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color = Gtk2::Gdk::Color->new (10000, 5000, 30000);
   $gc1->set_rgb_fg_color ($color);
   my $purple = Gtk2::Gdk::Color->parse ('purple');
   my $black = Gtk2::Gdk::Color->parse ('black');
   my $color2 = Gtk2::Gdk::Color->parse ('#E6EDBD');
   my $red = Gtk2::Gdk::Color->parse ('red');
   
   #my $desc = Gtk2::Pango::FontDescription->new();
   #my $context = Gtk2::Pango::Context->new();
   #my $layout = Gtk2::Pango::Layout->new ($context);
   my $layout = $widget->create_pango_layout ("");
   my $context = $layout->get_context;
   my $fontdesc = $context->get_font_description;
   $fontdesc->set_family ("Sans");
   
   if ( $enter_lay eq $false ){ # find the nice size and change the font only one time 
      my $min_width = 70;
      $width_lay = $min_width;
      $size = 5;
      while ( $width_lay <= $min_width ){  # to determine the minimum size
         $fontdesc->set_size ( $size * PANGO_SCALE );
         $layout->set_text ("00:00/00:00"); # only to get its width and height
         ($width_lay, $height_lay) = $layout->get_pixel_size; 
         $size = $size + 1;  
      } 
      $enter_lay = $true;
   } 
   else{
      $fontdesc->set_size ( ($size-1) * PANGO_SCALE );   
      #print "width_lay = $width_lay  ;; size = ",$size-1,"\n";       
   }
         
   #my $font = $context->load_font ($fontdesc); # font = Gtk2::Pango::Font
   #print " font = $font ;; family = ", $fontdesc->get_family,"\n"; 
   
   $layout->set_text ($data); # $data = "00:00"
   ($width_lay, $height_lay) = $layout->get_pixel_size;
   # print " width_lay, height_lay = $width_lay, $height_lay \n";  
   
   if ( $show_time_remaining eq $true ){
      $widget->window->draw_layout_with_colors ($gc1, $width - $width_lay - $corner/2 - 4,  $corner/4, $layout, $red, $color2);
      $layout->set_text ("-"); # negtive sinal
      $widget->window->draw_layout_with_colors ($gc1, $width - $width_lay - $corner/2 - 10, $corner/4, $layout, $red, $color2);
   }
   else{
      $widget->window->draw_layout_with_colors ($gc1, $width - $width_lay - $corner/2 - 4,  $corner/4, $layout, $black, $color2);
      $layout->set_text ("-"); # negtive sinal
      $widget->window->draw_layout_with_colors ($gc1, $width - $width_lay - $corner/2 - 10, $corner/4, $layout, $color2, $color2);
   }   
   return $true;
}

my $width_lay2; my $height_lay2;
my $enter_lay2 = $false; my $size2; # to determine the font size

sub draw_random {
   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea

   my $height = $widget->allocation->height;  # height of $widget $da
   my $width = $widget->allocation->width ;
   my $corner = 4;
				          
   my $gc1 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color = Gtk2::Gdk::Color->new (10000, 5000, 30000);
   $gc1->set_rgb_fg_color ($color);
   #my $blue = Gtk2::Gdk::Color->parse ('blue');   
   my $blue = Gtk2::Gdk::Color->new (25*256, 30*256, 240*256); 
   my $color2 = Gtk2::Gdk::Color->parse ('#E6EDBD'); #A7B1BE
   my $color3 = Gtk2::Gdk::Color->parse ('#A7B1BE'); 
   
   my $layout = $widget->create_pango_layout ("");
   my $context = $layout->get_context;
   my $fontdesc = $context->get_font_description;
   $fontdesc->set_family ("Sans");    
           
   if ( $enter_lay2 eq $false ){ # find the nice size and change the font only one time 
      my $min_width = 22;
      $width_lay2 = $min_width;
      $size2 = 5;
      while ( $width_lay2 <= $min_width ){  # to determine the minimum size
         $fontdesc->set_size ( $size2 * PANGO_SCALE );
         $layout->set_text ("rand"); # only to get its width and height
         ($width_lay2, $height_lay2) = $layout->get_pixel_size;
         #print "width_lay2 = $width_lay2  ;; size2 = $size2\n"; 
         $size2 = $size2 + 1;  
      } 
      $enter_lay2 = $true;
   } 
   else{
      # ($size-1) is the right size that adjust to minimum of 22 pixels.
      $fontdesc->set_size ( ($size2-1) * PANGO_SCALE );   
   }
   
   my ($label, $x, $y) = @data;
   $layout->set_text ($label);  
   
   if ( $label eq "rand" ){
      if ( $play_random eq $true ) {
         $widget->window->draw_layout_with_colors ($gc1, $x , $y, $layout, $blue, $color2);
      }
      else { 
         $widget->window->draw_layout_with_colors ($gc1, $x , $y, $layout, $color3, $color2);
      }
   }
   if ( $label eq "loop" ){
      if ( $loop_tracks eq $true ) {
         $widget->window->draw_layout_with_colors ($gc1, $x , $y - $height_lay2, $layout, $blue, $color2);
      }
      else { 
         $widget->window->draw_layout_with_colors ($gc1, $x , $y - $height_lay2, $layout, $color3, $color2);
      }
   } 
   return $true;
}


sub button_press_event {
  my ($widget, $event, $data) = @_;    # $widget = $da = Gtk2::DrawingArea

  my (undef, $x, $y, $state) = $event->window->get_pointer; 
  my $cd;
  if ( $use_audiocd eq $true ){ $cd = Audio::CD->init("$audiodevide_path"); }
  
  $go_back = $false;
  $go_forward = $false;
  
  if ($event->button == 1) {    
     my $height = $widget->allocation->height;
     my $width = $widget->allocation->width ;
     my $corner = 4;
     my $width_r = 8; my $height_r = 8;
     my $delta = 5;     # distance inter buttons
     my $dcentery = $height/2 - $height_r/2 - 2;
     my $dcenterx = 0;  # distance of rectangle from center of $widget
     
     # stop button display
     if ( abs($width - 2*$x + 2*$dcenterx) <= $width_r and abs($height - 2*$y + 2*$dcentery) <= $height_r  ){  
        # print "vc clicou em stop: x = $x, y = $y ;; height = $height ;; width = $width ;; dcentery = $dcentery\n";
	stop_playing_cda();
	show_time();
	$pause = $false;
	return;
     }
     # play button display
     $dcenterx = $width_r + $delta;       
     if ( abs($width - 2*$x + 2*$dcenterx) <= $width_r   and abs($height - 2*$y + 2*$dcentery) <= $height_r ){  		
	if ( $pause eq $false and $playing_audio_cd eq $false ){ play_selection();}
	elsif ( $cdcd_path ne "" ){
           my $cmd = "$cdcd_path -d $audiodevide_path resume ";
           exec_cmd_system($cmd);
        }
	elsif ( $use_audiocd eq $true ){ 
	   $cd->resume;
	}
	else{ 
	   $status_bar->push($context_id, " cdplay cant pause the cdrom! Use cdcd or Audio::CD.");
	   return;
	}
	$pause = $false;	
	return;		
     }
     # pause || button display
     $dcenterx = 2*($width_r + $delta);   # pause || label display
     if ( abs($width - 2*$x + 2*$dcenterx) <= $width_r and abs($height - 2*$y + 2*$dcentery) <= $height_r ){ 
        if ( $cdcd_path ne "" ){
           my $cmd = "$cdcd_path -d $audiodevide_path pause ";
           exec_cmd_system($cmd);
        }
	elsif ( $use_audiocd eq $true ){ 
	   $cd->pause;
	}
	else{ 
	   $status_bar->push($context_id, " cdplay cant pause the cdrom! Use cdcd or Audio::CD.");
	   return;
	}
	$pause = $true;	
	return;	
     }     
     # fastforward button display
     $dcenterx = 5*$width_r/2 + 3*$delta + 7*$width_r/8;   
     if ( abs($width - 2*$x + 2*$dcenterx) <= 7*$width_r/4 and abs($height - 2*$y + 2*$dcentery) <= $height_r ){
        #print "vc clicou em ff: x = $x, y = $y ;; height = $height ;; width = $width ;; dcenterx = $dcenterx\n"; 
	if ( $playing_audio_cd eq $false ){ return;}
        if ( $cdcd_path ne "" ){
           my $cmd = "$cdcd_path -d $audiodevide_path ff 00:15"; #advance 15 seconds
           exec_cmd_system($cmd);	   
        }
	elsif ( $use_audiocd eq $true ){ 
	   $cd->advance(0, 15);  #advance 15 seconds
	}
	else{ 
	   $status_bar->push($context_id, " cdplay cant fastforward the cdrom! Use cdcd or Audio::CD.");
	   return;
	}
	$count = $count+15;
	#show_time();
	$pause = $false;	
	return;	
     }       
     # rewind button display
     $dcenterx = 1*$width_r/2 + 1*$delta + 7*$width_r/8;   
     if ( abs($width - 2*$x - 2*$dcenterx) <= 7*$width_r/4 and abs($height - 2*$y + 2*$dcentery) <= $height_r ){ 
	if ( $playing_audio_cd eq $false ){ return;}
        if ( $cdcd_path ne "" ){
           my $cmd = "$cdcd_path -d $audiodevide_path rew 00:15"; #advance 15 seconds
           exec_cmd_system($cmd);	   
        }
	elsif ( $use_audiocd eq $true ){ 
	   $cd->advance(0, -15);  #advance 15 seconds
	}
	else{ 
	   $status_bar->push($context_id, " cdplay cant rewind the cdrom! Use cdcd or Audio::CD.");
	   return;
	}
	if ($count-15 >= 0){ $count = $count-15;}
	else { $count = 0; 
	       $go_back = $true;
	}
	#show_time();
	$pause = $false;	
	return;	
     }      
     # eject button display
     $dcenterx = 6*$width_r/2 + 4*$delta + 7*$width_r/4;
     if ( abs($width - 2*$x + 2*$dcenterx) <= $width_r and abs($height - 2*$y + 2*$dcentery) <= $height_r ){ 
        stop_playing_cda();
        if ( $cdcd_path ne "" ){
           exec_cmd_system("$cdcd_path -d $audiodevide_path eject");
	   # system("/usr/bin/eject /dev/hdc") || die " Error: $! \n";
	   # print " here, eject\n";	   
        }
	if ( $use_audiocd eq $true ){ $cd->eject; }
	exec_cmd_system("eject $audiodevide_path"); 
	return;	
     }       
     # rand label layout
     if ( $x <= ($width_lay2 + $corner/2 + 2) and $y <= $corner/2 + $height_lay2 ){  
	$play_random ^= 1; 
	draw_random($da,undef,("rand", $corner/2 + 2, $corner/2));   
        show_time();
        return;		
     }
     # loop label layout
     if ( $x <= ($width_lay2 + $corner/2 + 2) and $y > $height - $corner/2 - $height_lay2 ){   
	$loop_tracks ^= 1;
        draw_random($da,undef,("loop", $corner/2 + 2, $height - $corner/2 ));
        return;		
     }      
     # time min:sec display
     if ( $x >= $width - $width_lay - $corner - 4  and $y < $height_lay + $corner/4 ){   
	$show_time_remaining ^= 1;
        show_time();
        return;		
     }    
  }
  # We've handled the event, stop processing
  return $true;
}


my $button_go_forward = Gtk2::Button->new_from_stock ("gtk-go-forward");
my $image = Gtk2::Image->new_from_stock ('gtk-go-forward', 'menu');
$button_go_forward->set( 'focus-on-click' => $false, 'relief' => 'none' );
$button_go_forward->signal_connect ('clicked', sub {  
if ( $playing_audio_cd eq $true ){ $go_forward = $true; }
else { $go_forward = $false; }
} );
$button_go_forward->set_size_request( 34, -1 );
$button_go_forward->set_sensitive($false);
$hbox4->pack_start ($button_go_forward, $false, $false, 0);
$button_go_forward->show;

sub entry_cda_change {
   $button_selec->set_sensitive($false);
   $button_unselec->set_sensitive($false);
   $button_go_back->set_sensitive($false); 
   $button_go_forward->set_sensitive($false);  
   $da->set_sensitive($false);
   $file_extension = 'error'; # rip audio cd is not possible   
   stop_playing_cda();
}

###---------------------- Frame 5 --------------------###
#--------------------------About------------------------#
my $buffer_about = Gtk2::TextBuffer->new ();
$buffer_about->create_tag ("italic",         'foreground' => "red",   # see <man Gtk2::TextTag> for more options
                                            #'background' => "yellow",
                                            justification => 'center', 
                                                     font => "Helvetica Italic 13");
$buffer_about->create_tag ("version",        'foreground' => "blue",   
                                            justification => 'center',
					             font => "Helvetica 11");
$buffer_about->create_tag ("descr",          'foreground' => "black",  
                                            justification => 'center',
					             font => "Sans 9");						     

# Construct a textview to put About
my $textview_about = Gtk2::TextView->new_with_buffer ($buffer_about);
$textview_about->set (editable => $false, cursor_visible => $false, 'wrap-mode' => 'word');
my $color_about = Gtk2::Gdk::Color->parse ("#E6EDBD");  
$textview_about->modify_base ('normal', $color_about);
$textview_about->show;

$align = Gtk2::Alignment->new(0.5, 0.5, 1.0, 1.0);
$align->set_border_width (4);
$align->add ($textview_about);
$align->show;

# Event box is to add a green border around each child view
my $event_box_about = Gtk2::EventBox->new;
my $color_event_about = Gtk2::Gdk::Color->parse ("#A0B8A3");
$event_box_about->modify_bg ('normal', $color_event_about);
$event_box_about->add ($align);
$frame5->add( $event_box_about );
$event_box_about->show;

$textview_about->get_buffer->delete($textview_about->get_buffer->get_bounds);
my $iter_about = $buffer_about->get_iter_at_offset (0); # get start of buffer 
$buffer_about->insert_with_tags_by_name ($iter_about, "gnormalize", "italic");
$buffer_about->insert_with_tags_by_name ($iter_about, " - ", "descr");
$buffer_about->insert_with_tags_by_name ($iter_about, "version $VERSION\n", "version");


my $str = "gnormalize is a front end to normalize, a ripper, an encoder and an audio converter. It decodes";
$str .= " the files to wave, then normalizes the wave and re-encodes it. gnormalize can also convert";
$str .= " audio format between MP3, OGG, MPC, APE and FLAC; change the encoding and ID3 tag properties."; 
	   
$buffer_about->insert_with_tags_by_name ($iter_about, $str, "descr");

$str = "\nHomepage: $HOMEPAGE\nThis software is available under GNU General Public Licence.";

$buffer_about->insert_with_tags_by_name ($iter_about, $str, "descr");

$str = "\n\nAuthor: $AUTHOR\nemail: claudiofsr\@yahoo.com\ndate: $DATE ; So Paulo - Brasil";

$buffer_about->insert_with_tags_by_name ($iter_about, $str, "descr");


######--------------------------------------------######

#####------------- h b o x _normalize--------------#####
###--------------------------------------------------###

# debug button
my $button_debug = Gtk2::ToggleButton->new ('Debug');
$button_debug->signal_connect( "clicked", 
sub { 
    if (not $button_debug->get_active) { 
           $frame_debug->hide;
    }
    else { $frame_debug->show;     
    }
});
# $hbox->pack_start( $child, $expand, $fill, $padding );
$hbox_normalize->pack_start( $button_debug, 0, 0, 0 );
$button_debug->show;

my $normalize_button = Gtk2::ToggleButton->new("normalize");
$normalize_button->signal_connect( "toggled", \&toggle_button_normalize );
# a tabela ir expandir de acordo com o tamando do vbox
#my $color_green = Gtk2::Gdk::Color->parse ("#A0B8A3");
#$normalize_button->modify_bg ('normal', $color_green);
$hbox_normalize->pack_start( $normalize_button, 1, 1, 0 );
$normalize_button->show();

# to be implemented: Thread. See man <Thread::Queue>, 
# <man threads::shared> and tutorial <man perlthrtut>

my $rip_sucess = $false; # get $true if rip is completed

# this is the brain that control all processes
sub toggle_button_normalize { 
    
    if ( $type eq "Fix" ) {  # String comparison - see man perlintro
                $value=$spinner->get_value_as_int();
                #print ("normalization value = $value\n");
    }

    if ( $file_extension eq 'other' ){
        $normalize_button->set_active($false);
	$status_bar->push($context_id, " Choose a mp3/mpc/ogg/ape/flac/wav/cda file to normalize!");
	return $false;
    }
    if ( $file_extension eq 'error' ){
        $normalize_button->set_active($false);
	$status_bar->push($context_id, " Dependence not satisfied!");
	return $false;
    }
    
    # not change the input and output frames while any encoding process
    $frame11->set_sensitive($false);  
    $frame12->set_sensitive($false);
    
    # if the file choosed is a supported file and the normalize button is active
    # $button_dir->get_active - normalize all files in the directory
    if ($button_dir->get_active and $normalize_button->get_active and $file_extension ne 'cda'){ 
	for (my $i=1;$i<=@array_of_files;$i++){  # @array is it size
	     my $file = $array_of_files[$i-1];
	     
	     ($file_mp3,$file_ogg,$file_mpc,$file_ape,$file_flac,$file_wav,$file_wav_copy) = file_with_extension($file);
		  
	     # decode, normalize and encode the files
	     dec_norm_enc($file_mp3,$file_ogg,$file_mpc,$file_ape,$file_flac,$file_wav,$file_wav_copy); 
	     #abandon the sub if button <stop normalize> is pressed
	     if ( not $normalize_button->get_active) {return $false;}
	 } # for - final
    } #final of:  if ($button_dir->get_active and $normalize_button->get_active)
    
    # normalize only one file in the directory
    if ( $button_file->get_active and $normalize_button->get_active and $file_extension ne 'cda' ){
        dec_norm_enc($file_mp3,$file_ogg,$file_mpc,$file_ape,$file_flac,$file_wav,$file_wav_copy); # decode, normalize and encode the files
	# abandon the sub when the button <stop normalize> is pressed
	if ( not $normalize_button->get_active) {return $false;}
    }
    
    my $one_rip = $false;
    if ( $file_extension eq 'cda' and $normalize_button->get_active ){      
        my $at_least_one_music_selected = $true;
       
        while ( $at_least_one_music_selected eq $true and $normalize_button->get_active ){
            $at_least_one_music_selected = $false;
	  
            for (my $row=0; $row <= $#music_cda ; $row++){ 
               # if some track is toggled, then rip it       
               if ($music_cda[$row]{rip} eq $true ){
	          $at_least_one_music_selected = $true;
		  last; #abandon the for loop
	       }	   
            }
	    if ( $at_least_one_music_selected eq $false and @music_cda >= 1 and $one_rip eq $false ){ 
	       $status_bar->push($context_id, " Select at least one track to rip!");
	    }
            if ( $at_least_one_music_selected eq $true ){
    	       search_track_and_rip ();
	       $one_rip = $true;
	    }
        } # final of while loop 
    }               
    # Final messages
    if ($file_extension eq 'cda' and $one_rip eq $true){  
        if ($rip_sucess eq $true and $normalize_button->get_active){ 
	       $status_bar->push($context_id, " Success! Rip finished.");
	}
        else { $status_bar->push($context_id, " Rip stopped!");}
    }
    if ($file_extension ne 'cda'){
        if ($type eq "None"){  $status_bar->push($context_id, " Success!");}
        elsif ($file_extension ne $file_output){ $status_bar->push($context_id, " Success! File converted.");}
	else { $status_bar->push($context_id, " Success! File normalized.");}
    }
    # if the button <stop normalize> is pressed the normalize_button is
    # not set to false automaticaly, then:
    $normalize_button->set_active($false); 
    # return the input and output frames to original state after processes
    $frame11->set_sensitive($true);  
    $frame12->set_sensitive($true);
    # After stop the process, label is changed.
    if ($file_extension eq 'cda'){
        $normalize_button->set_label("rip + normalize + encode");  } 
    else { $normalize_button->set_label("normalize"); }     
} # final of ''toggle_button_normalize''

sub search_track_and_rip
{	  
   my $model = $treeview4->get_model;
    	      
   for (my $row=0; $row <= $#music_cda ; $row++){ 
            
      # find $iter and $path associeted to some $track
      my $track = sprintf("%02d", $row + 1 );  # 02d : leading zero
      my ($iter,$path) = search_iter_path_for_track($track);
          
      if ($music_cda[$row]{rip} and $normalize_button->get_active){  # if the music is toggled, then rip it
             my $file_wav;
	     stop_playing_cda();	       	  
	     $normalize_button->set_label("stop ripping");
	     # set the artist of the music; get it from info of gnormalize or cddb
	     if ( $entry_artist->get_text eq "" ){ 
	            $entry_artist->set_text($music_cda[$row]{artist});}
	     else { 
	            $music_cda[$row]{artist} = $entry_artist->get_text; 
	            $model->set ($iter, COLUMN_ARTIST, $music_cda[$row]{artist});
	     }
	     # set the title of the music
	     if ( $entry_title->get_text eq "" ){ 
	            $entry_title->set_text($music_cda[$row]{title});}
	     else { 
	            $music_cda[$row]{title} = $entry_title->get_text; 
	            $model->set ($iter, COLUMN_TITLE, $music_cda[$row]{title}); 
	     }
	     
	     $total_track = $#music_cda + 1;	     
	     $entry_tn->set_text($track);
	     $entry_tt->set_text($total_track);
	     $file_wav = "$music_cda[$row]{artist}-$track-$music_cda[$row]{title}.wav";  #filename
	     
	     # ripping start here  ;;  $rip_sucess get $true if the rip is completed
	     if ($ripper eq "cdparanoia"){ $rip_sucess = rip_audio_cdparanoia($track,$file_wav);} 
	     elsif ($ripper eq "cdda2wav"){$rip_sucess = rip_audio_cdda2wav($track,$file_wav);  }   
	   
	     if ( $normalize_button->get_active ){  # the rip was successfuly completed
                    $music_cda[$row]{rip} = $false; # change the boolean value
                    $model->set ($iter, COLUMN_RIP, $music_cda[$row]{rip}); # 'untaggle' the ripped music
		      
	            ($file_mp3,$file_ogg,$file_mpc,$file_ape,$file_flac,$file_wav,$file_wav_copy) = file_with_extension($file_wav);
                    #$directory = exec_cmd_system("pwd");
		    # decode, normalize and encode the files
                    dec_norm_enc($file_mp3,$file_ogg,$file_mpc,$file_ape,$file_flac,$file_wav,$file_wav_copy);
                    # abandon the if condition when the button <stop normalize> is pressed
                    if ( not $normalize_button->get_active) {last;}
	     } 
	     $entry_title->set_text(""); #erase entry
	     $entry_artist->set_text("");
      }
      #abandon the for loop if button <stop normalize> is pressed
      if ( not $normalize_button->get_active ) { last;}  
   } # final of ''for (my $i=0; $i <= $#music_cda ; $i++){ ''
}

## decode, normalize and encode - for mp3/ogg/wav files
sub dec_norm_enc {
       my $file_mp3 = shift;   # in this order: $file_mp3 ; $file_ogg ; $file_mpc ; 
       my $file_ogg = shift;   # $file_ape ; $file_flac ; $file_wav ; $file_wav_copy
       my $file_mpc = shift;
       my $file_ape = shift;
       my $file_flac = shift;
       my $file_wav = shift;
       my $file_wav_copy = shift;
       my $file;
       my $insensibility = $spinner_sensi->get_value;
       $priority = $spinner_pri->get_value; #priority
       
       if ( $file_extension eq 'mp3' ){ $file = $file_mp3; }
       if ( $file_extension eq 'ogg' ){ $file = $file_ogg; }
       if ( $file_extension eq 'mpc' ){ $file = $file_mpc; }
       if ( $file_extension eq 'ape' ){ $file = $file_ape; }
       if ( $file_extension eq 'flac'){ $file = $file_flac;}
       if ( $file_extension eq 'wav' ){ $file = $file_wav; }
       
       if ($file_extension ne 'cda'){
          # for the case when the file is deleted or changed after pressed the normalize_button
          if ( not -e encode_char("$directory/$file")){ 
              $status_bar->push($context_id, " Cant find $file_extension file to normalize!");
              return $false;}
       }
       refresh_selection();   # refresh progress bar names
       
       if ( $file_extension =~ /(mp3|ogg|flac|mpc)/ ){ get_info_from_file(); } 
       
       if ( $file_extension =~ /(wav|ape|cda)/ ){ 
           get_info_from_entries(); }  # get info from entry "info " of gnormalize
       
       # decode, normalize and encode the files
       # decode:
       $normalize_button->set_label("stop decoding");
       if ( $file_extension eq 'mp3' and $lame_path ne ""   ){ decode_mp3_to_wav(); }
       if ( $file_extension eq 'ogg' and $oggdec_path ne "" ){ decode_ogg_to_wav(); } 
       if ( $file_extension eq 'mpc' and $mpcdec_path ne "" ){ decode_mpc_to_wav(); } 
       if ( $file_extension eq 'ape' and $ape_path ne ""    ){ decode_ape_to_wav(); }    
       if ( $file_extension eq 'flac' and $flac_path ne ""  ){ decode_flac_to_wav();}  
       
       # normalize:
       if ( $normalize_button->get_active ) { 
            $normalize_button->set_label("stop normalizing");
	    normalize_wav();
       }    
       
       # encode:
       # if $check_button_change not active and file is already normalized and has the same extension then make copy,
       # otherwise the file will be encoded.
       # the output can be mp3 or ogg 
       if ( $normalize_button->get_active ) {
         if ( $file_extension eq $file_output and ( $already_normalized or abs($adjust) <= $insensibility ) and
            $type ne "None" and not $check_button_change->get_active ){
           # then make copy
	   # note that $file_wav and .cda never satisfy this conditons
	   if ( abs($adjust) <= $insensibility ){ same_extension_overwrite(); }
         }
         # $file_wav always satisfy this conditon
         else { 
           $normalize_button->set_label("stop encoding");
           if ( $button_output_mp3->get_active ) {encode_wav_to_mp3();}
           if ( $button_output_ogg->get_active ) {encode_wav_to_ogg();}
	   if ( $button_output_mpc->get_active ) {encode_wav_to_mpc();}
	   if ( $button_output_ape->get_active ) {encode_wav_to_ape();}
	   if ( $button_output_flac->get_active) {encode_wav_to_flac();}
         } 
       }
}

# get all file extensions from original filename
sub file_with_extension {
   my $file = shift;

   ($file_mp3 = $file) =~ s/\.(.){3,4}$/.mp3/i; # to obtain the filename with appropriate extension
   ($file_ogg = $file) =~ s/\.(.){3,4}$/.ogg/i;
   ($file_mpc = $file) =~ s/\.(.){3,4}$/.mpc/i;
   ($file_ape = $file) =~ s/\.(.){3,4}$/.ape/i;
   ($file_flac = $file) =~ s/\.(.){3,4}$/.flac/i;
   ($file_wav = $file) =~ s/\.(.){3,4}$/.wav/i;
   ($file_wav_copy = $file) =~ s/\.(.){3,4}$/-normalized.wav/i; #change file extension to .wav
		  
   # return to the real extension name; for that cases *.mP3, Mp3, ...		  
   if ( $file_extension eq 'mp3'){ $file_mp3 = $file; } 
   if ( $file_extension eq 'ogg'){ $file_ogg = $file; } # return to original extension  
   if ( $file_extension eq 'mpc'){ $file_mpc = $file; }
   if ( $file_extension eq 'ape'){ $file_ape = $file; }
   if ( $file_extension eq 'flac'){ $file_flac = $file; }
   if ( $file_extension eq 'wav'){ $file_wav = $file; }
		  
   $entry_f->set_text( "$file" ); # refresh file to normalize
   $tooltips->set_tip( $entry_f, "$file");
   	     
   return ($file_mp3,$file_ogg,$file_mpc,$file_ape,$file_flac,$file_wav,$file_wav_copy)
}

## stop a process by the pid number
sub StopProcessing {  # I see dvd2dvix3pass code with some changes 
       my ($pid,$mesg) = @_;
       ## get lame (or normalize,oggdec,oggenc) pid
       ## try to kill pid+1, else pid
       #print ("\n $mesg which pid = $pid\n");
       if( $pid >= 0 )  # if some $pid >=0 then some process is running
       {
	   ## kill the lame/normalize/... PID
	   ++$pid;
	   my $processeskilled = kill 9, $pid;
	   if( $processeskilled == 0 )
	   {
	      --$pid;
	      print "STOP:: pid ".($pid+1)." failed, trying kill pid $pid \n";
	      $processeskilled = kill 9, $pid;
	      if( $processeskilled == 0 )
	      {   print "Could not $mesg : pid = $pid\n";}
           }
	}
	# In the case of stop process, always remove file_wav_copy if it exist
	if ( -e encode_char("$directory/$file_wav_copy") ){ 
            exec_cmd_system("rm -f \"$directory/$file_wav_copy\" ");
	}
	#remove wav file if button_del_wav is active and $file_wav exist
	if ($check_button_del_wav->get_active and -e encode_char("$directory/$file_wav") ){ 
	    exec_cmd_system("rm -f \"$directory/$file_wav\" "); 
	}		
	# In the case of stop normalize process, a file like _normAAAAAA is created,
	# them it will be removed, if it exist.	my $file_norm =~ /^\_norm(\w){6}$/i;
	if ( -e encode_char("$directory/_normAAAAAA") ){ 
            exec_cmd_system("rm -f \"$directory/_normAAAAAA\" ");
	}
	$status_bar->push($context_id, $mesg);
	insert_msg("$mesg", "small-red");
	insert_msg("\n" , "small");
	return -1; # set (pid = -1) assignnig to a stopped process
}

###--------------------------------------------------###

# Create "Quit" button using the icon and text from 
# the specified stock item, see Gtk2::Stock
# print ( "Stoks: ",Gtk2::Stock->list_ids ,"\n");
$button = Gtk2::Button->new_from_stock ('gtk-quit');
$button->signal_connect( "clicked", \&Quit );
$hbox_normalize->pack_start( $button, 0, 0, 0 );
$button->show();

#####----------------T a b l e _ down--------------#####
###--------------------------------------------------###

my $da_arrow_dec = Gtk2::DrawingArea->new;
# set a minimum size
$da_arrow_dec->set_size_request (78, 20); 
$table_down->attach ($da_arrow_dec, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
$da_arrow_dec->signal_connect (expose_event => \&draw_arrow_dec);		
$da_arrow_dec->show;
 
$change_fonte = $true;
my $size_input;

sub draw_arrow_dec {
   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
  				          	   				          
   # to expose this event  
   if ( $file_extension eq 'other' ){ draw_input_output($da_arrow_dec,undef,"mp3","wav"); } # default
   else { draw_input_output($da_arrow_dec,undef,("$file_extension","wav")); }
    
   return $true;
}

sub draw_input_output {
   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
  				          
   my $height = $widget->allocation->height;  # height of $widget $da
   my $width = $widget->allocation->width ;   
    
   my $gc = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color = Gtk2::Gdk::Color->new (184*256, 211*256, 193*256); # #E6E7E6 = 230 231 230  --- 184 211 193
   $gc->set_rgb_fg_color ($color);
   my $gc1 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color1 = Gtk2::Gdk::Color->new (100*256, 100*256, 200*256); #RGB = 85 90 190
   $gc1->set_rgb_fg_color ($color1);   
   my $purple = Gtk2::Gdk::Color->parse ('purple');
   my $blue = Gtk2::Gdk::Color->parse ('blue');
   my $color3 = Gtk2::Gdk::Color->parse ('#E6EDBD');	
   
   $widget->window->draw_rectangle ($gc, $true, 0, 0, $width, $height); 					        				      				   
   
   my $border = 5;  
   my $width_a = $height - 2*$border;
   # arrow
   $widget->window->draw_rectangle ($gc1, $true, $width/2 - 7, $height/2 - 2, 8, 4); 
   $widget->window->draw_polygon ($gc1, $true, 
                                  $width/2, $height - $border, 
				  $width/2, $border,
                                  $width/2 + $width_a, $height/2 );
   	   				         
   my $layout = $widget->create_pango_layout ("");
   my $context = $layout->get_context;
   my $fontdesc = $context->get_font_description;
   $fontdesc->set_family ("Sans");
   
   if ( $change_fonte eq $true ){ # find the nice size and change the font only one time 
      my $min_width = 20;
      $width_lay = $min_width;
      $size_input = 2;
      while ( $width_lay <= $min_width ){  # to determine the minimum size
         $fontdesc->set_size ( $size_input * PANGO_SCALE );
         $layout->set_text ("wav"); # only to get its width and height
         ($width_lay, $height_lay) = $layout->get_pixel_size; 
         $size_input = $size_input + 1;  
      } 
      $change_fonte = $false;
   } 
   else{ $fontdesc->set_size ( ($size_input - 1) * PANGO_SCALE ); } 
   
   #print " data_0 = $data[0] ;; data_1 = $data[1] ;; size = ",$size_input-1,"\n"; 
       
   $layout->set_text ($data[0]); # $data = "mp3"
   ($width_lay, $height_lay) = $layout->get_pixel_size; 
   $widget->window->draw_layout_with_colors ($gc1, 2, $height/2 - $height_lay/2, $layout, $blue, $color);
   
   $layout->set_text ($data[1]); # $data = "wav"
   ($width_lay, $height_lay) = $layout->get_pixel_size; 
   $widget->window->draw_layout_with_colors ($gc1, $width - $width_lay - 2, $height/2 - $height_lay/2, $layout, $blue, $color); 
       
   return $true;
}


########-------------Progress-Bar----------------#######
# see acidrip and dvd2divx3pass for suggestion
# number line 851 of acidrip.pm - version 0.14
# number line 542 of helpers.pm at dvd2divx3pass - version 1.0b4

# Create the Gtk2::ProgressBar
$pbar = Gtk2::ProgressBar->new;
$table_down->attach_defaults ($pbar, 1, 4, 0, 1);
$pbar->set_size_request( 420, -1 ); # width, height 
#$pbar->set_text($file_mp3);
$pbar->set_orientation('left_to_right');
$pbar->show;


my $da_norm = Gtk2::DrawingArea->new;
# set a minimum size
$da_norm->set_size_request (76, 20); 
$table_down->attach ($da_norm, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
$da_norm->signal_connect (expose_event => \&draw_norm);		
$da_norm->show;

$change_fonte_norm = $true;
my $size_norm;
 
sub draw_norm {
   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
  				          
   my $height = $widget->allocation->height;  # height of $widget $da
   my $width = $widget->allocation->width ;   
    
   my $gc = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color = Gtk2::Gdk::Color->new (184*256, 211*256, 193*256); # #E6E7E6 = 230 231 230
   $gc->set_rgb_fg_color ($color);
   my $gc1 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
   my $color1 = Gtk2::Gdk::Color->new (100*256, 100*256, 200*256); #RGB = 85 90 190
   $gc1->set_rgb_fg_color ($color1);   
   my $purple = Gtk2::Gdk::Color->parse ('purple');
   my $blue = Gtk2::Gdk::Color->parse ('blue');
   my $color3 = Gtk2::Gdk::Color->parse ('#E6EDBD');	
   	   				      
   $widget->window->draw_rectangle ($gc, $true, 0, 0, $width, $height); 					        				      				   
   
   my $border = 5;  
   my $width_a = 10;  
   # arrow
   $widget->window->draw_rectangle ($gc1, $true, $width/2 - 6, $height/2 - 2, 8, 4); 
   $widget->window->draw_polygon ($gc1, $true, 
                                  $width/2 - $width_a/2 +5, $height - $border, 
				  $width/2 - $width_a/2 +5, $border,
                                  $width/2 + $width_a/2 +5, $height/2 );  
   
   my $layout = $widget->create_pango_layout ("");
   my $context = $layout->get_context;
   my $fontdesc = $context->get_font_description;
   $fontdesc->set_family ("Sans");
   
   if ( $change_fonte_norm eq $true ){ # find the nice size and change the font only one time 
      my $min_width = 60;
      $width_lay = $min_width;
      $size_norm = 2;
      while ( $width_lay <= $min_width ){  # to determine the minimum size
         $fontdesc->set_size ( $size_norm * PANGO_SCALE );
         $layout->set_text ("normalize"); # only to get its width and height
         ($width_lay, $height_lay) = $layout->get_pixel_size; 
         $size_norm = $size_norm + 1;  
      } 
      $change_fonte_norm = $false;
   } 
   else{ $fontdesc->set_size ( ($size_norm - 1) * PANGO_SCALE ); } 
   
   $layout->set_text ("normalize"); 
   ($width_lay, $height_lay) = $layout->get_pixel_size; 
   $widget->window->draw_layout_with_colors ($gc1, $width/2 - $width_lay/2, $height/2 - $height_lay/2, $layout, $blue, $color); 
      
   return $true;
}


# Create the Gtk2::ProgressBar
$pbar_n = Gtk2::ProgressBar->new;
$table_down->attach_defaults ($pbar_n, 1, 4, 1, 2);
$pbar_n->set_orientation('left_to_right');
$pbar_n->show;

my $da_arrow_enc = Gtk2::DrawingArea->new;
# set a minimum size
$da_arrow_enc->set_size_request (76, 20); 
$table_down->attach ($da_arrow_enc, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
$da_arrow_enc->signal_connect (expose_event => \&draw_arrow_enc);		
$da_arrow_enc->show;
 
sub draw_arrow_enc {
   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea  				         
    
   # to expose this event   
   draw_input_output($da_arrow_enc,undef,("wav","$file_output"));
    
   return $true;
}

# Create the Gtk2::ProgressBar
$pbar_lame = Gtk2::ProgressBar->new;
$table_down->attach_defaults ($pbar_lame, 1, 4, 2, 3);
$pbar_lame->set_orientation('left_to_right');
$pbar_lame->show;


##-----------------------------------------------------##
## ----------------- hbox + TextView ----------------- ##

# Inside the vbox we put frame_debug
$frame_debug = new Gtk2::Frame();
$frame_debug->set_label_align( 1.0, 0.0 );
$frame_debug->set_shadow_type( 'etched_out' );
$vbox->add($frame_debug);

$vbox_text = new Gtk2::VBox;
$frame_debug->add( $vbox_text );
$vbox_text->set_border_width(2);
$vbox_text->show;

# on hbox we put a button_clear_debug
$hbox_text = new Gtk2::HBox;
$vbox_text->pack_start( $hbox_text, 0, 0, 0 );
$hbox_text->show;

# Create a centering alignment object;
$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
$hbox_text->add($align);
$align->show;

# debug clear button
$button_clear = Gtk2::Button->new_from_stock ('gtk-clear');
$button_clear->signal_connect( "clicked", 
sub { 
       # delete buffer   -  $buffer->delete ($start, $end)
       # " (start, end) = $buffer->get_bounds  -  Retrieves the first and 
       # last iterators in the buffer, i.e. the entire buffer lies within 
       # the range (start,end). " 
       $textview->get_buffer->delete($textview->get_buffer->get_bounds);
       $iter = $buffer->get_iter_at_offset (0); # get start of buffer  
});
$align->add($button_clear);
$button_clear->show;

##--------------------TextView-------------------------##

# Construct an command output text
$textview = new Gtk2::TextView;
$textview->set_wrap_mode('word'); # 'none'
# or $textview->set_editable($false);
$textview->set (editable => $false, cursor_visible => $false);
$textview->show;

$scrolled = new Gtk2::ScrolledWindow( undef, undef );
$scrolled->set_policy( 'automatic', 'always' );
$scrolled->set_size_request( -1, 130 );
$scrolled->add($textview);
$scrolled->set_shadow_type('GTK_SHADOW_IN'); # 'in' , 'GTK_SHADOW_IN'

$vbox_text->add($scrolled);
$scrolled->show;

# see <man Gtk2::TextBuffer>
# $buffer = Gtk2::TextBuffer->new;
$buffer = $textview->get_buffer;
$buffer->create_tag ("italic",       'foreground' => "red", 
                                    #'background' => "yellow",
                                    justification => 'center', 
                                            style => 'italic');
$buffer->create_tag ("small",        'foreground' => "blue", 
                                    'size-points' => "10");
$buffer->create_tag ("small-green",  'foreground' => "green",
                                    justification => 'center',
                                    'size-points' => "10");
$buffer->create_tag ("small-black",  'foreground' => "black",
                                    justification => 'center',
                                    'size-points' => "10");
$buffer->create_tag ("black",        'foreground' => "black",
                                    justification => 'left',
                                    'size-points' => "10");				    
$buffer->create_tag ("red",          'foreground' => "red",
                                    justification => 'left',
                                    'size-points' => "11");				    			      
$buffer->create_tag ("small-red",    'foreground' => "red",
                                    justification => 'center', 
                                    'size-points' => "10");
$buffer->create_tag ("small-ini",  'foreground' => "red",
                                    justification => 'left',
                                    'size-points' => "20");				    
				    			      			      
# "get start of buffer; each insertion will revalidate the
# iterator to point to just after the inserted text."
$iter = $buffer->get_iter_at_offset (0);
$buffer->insert_with_tags_by_name ($iter, "gnormalize", "italic");
$buffer->insert_with_tags_by_name ($iter, " $VERSION \n\n" , "small");
#$buffer->insert ($iter , " - author: $AUTHOR");

# see /usr/share/doc/perl-Gtk2-1.054/examples/thread_usage.pl
# see <man Gtk2::TextIter>, <man Gtk2::Gdk::Threads>

sub insert_msg {    # scrolls down the text in the textboxes
        my $msg = shift;
	my $tag_name = shift;
	# (start, end) = $buffer->get_bounds
	$iter = $buffer->get_end_iter;
	# $iter = $textview->get_buffer->get_end_iter;
	if ($tag_name) {
                $buffer->insert_with_tags_by_name($iter, $msg, $tag_name);}
        else {  $buffer->insert($iter, $msg);} # if dont have tag
	$iter = $buffer->get_end_iter;  # or $iter = $buffer->get_bounds;
	#$text_view->scroll_to_iter ($iter, $within_margin,$use_align, $xalign, $yalign)
	$textview->scroll_to_iter ($iter, 0.0, $false, 0.0, 0.0);	
	while (Gtk2->events_pending()) { Gtk2->main_iteration()}; # to update
}

# only for illustration
sub get_text {
	my $buffer = shift->get_buffer;
	$buffer->get_text ($buffer->get_start_iter, $buffer->get_end_iter, $true);
	# print (get_text($textview) );
}

##-----------------------------------------------------##
## --------------------- Statusbar ------------------- ##

$status_bar = Gtk2::Statusbar->new; 
$vbox->pack_start( $status_bar, 0, 0, 0 );
$status_bar->set_has_resize_grip ($false);
#$vbox->add($status_bar);
$status_bar->show;

#####--------------------------------------------------#####
#####------------------ Subroutines -------------------#####

sub dir_selection {

# repair the standard cancel label
my @items = ( { stock_id => "gtk-cancel", label => "_Cancel"     } );
Gtk2::Stock->add (@items);  # Register our stock items

# Create a new file selection widget
$file_dialog = new Gtk2::FileSelection( "Select a mp3, ogg, mpc, ape, flac, or wave file" );
$file_dialog->signal_connect( "destroy", sub { $file_dialog->hide; return $true });
# tell it to allow multiple selections...
$file_dialog->set_select_multiple ($false);
$file_dialog->set('show-fileops' => $true);
#abrir no lacal especfico
$file_dialog->set_filename("$directory/");
# match a pattern to a filename 
$file_dialog->complete(" ");

# print $file_dialog->file_list->get_model;  # Gtk2::TreeView=HASH(0x81e4c50) ;; "Gtk2::TreeView"

# Connect the ok_button to file_ok_sel function
$file_dialog->ok_button->signal_connect( "clicked", \&ok_b, $file_dialog );
sub ok_b {
   my ( $widget, $file_dialog ) = @_;   
   
   my $file;  
   
   ($file = $file_dialog->get_filename) =~ s/(.*\/)//g;      #copy and remove (path)/
   insert_msg("Selected file = $file\n\n" , "small");
   
   ($directory = $file_dialog->get_filename) =~ s/(.*)\/.*/$1/g;
   
   # suppose file = music.Mp3   or  music.MP3  or music.mP3  
   if ( $file =~ /\.mp3$/i ){       # verify if file extension is .mp3 
          $file_extension = 'mp3';
	  $file_mp3 = $file; }      # for files like: music.Mp3, music.MP3  or music.mP3 
   elsif ( $file =~ /\.ogg$/i ){ 
          $file_extension = 'ogg';
	  $file_ogg = $file; }      # for files like: music.OGG, music.OgG  or music.oGG or ... 
   elsif ( $file =~ /\.mpc$/i ){ 
          $file_extension = 'mpc';
	  $file_mpc = $file; } 
   elsif ( $file =~ /\.ape$/i ){ 
          $file_extension = 'ape';
	  $file_ape = $file; } 
   elsif ( $file =~ /\.flac$/i ){ 
          $file_extension = 'flac';
	  $file_flac = $file; } 	  	   
   elsif ( $file =~ /\.wav$/i ){ 
          $file_extension = 'wav'; 
	  $file_wav = $file; } 
   else{  $file_extension = 'other'}
     
   ($file_mp3,$file_ogg,$file_mpc,$file_ape,$file_flac,$file_wav,$file_wav_copy) = file_with_extension($file);
          
   get_info_from_file();           # refresh mp3/ogg/flac info
   $entry_d->set_text( "$directory" );
   $tooltips->set_tip( $entry_d, "$directory");
   $normalize_button->set_label("normalize");
   
   if ( $file_extension eq 'other' ){
        $status_bar->push($context_id, " Choose files with mp3, mpc, ogg, ape, flac or wav extension!");
        pbar_erase_name();  # erase progress bar names
	$file_dialog->hide; 
        return $false;
   }	
   if ($file_extension eq 'mp3' and $lame_path eq "") {
       $status_bar->push($context_id, " Cant choose mp3 format! lame not found.");
       pbar_erase_name();  # erase progress bar names
       $file_dialog->hide;
       $file_extension = 'error';
       return $false;
   }
   if ($file_extension eq 'ogg' and $oggdec_path eq "") {
       $status_bar->push($context_id, " Cant choose ogg format! oggdec not found.");
       pbar_erase_name();  # erase progress bar names
       $file_dialog->hide;
       $file_extension = 'error';
       return $false;
   }
   if ($file_extension eq 'mpc' and $mpcdec_path eq "") {
       $status_bar->push($context_id, " Cant choose mpc format! mppdec not found.");
       pbar_erase_name();  # erase progress bar names
       $file_dialog->hide;
       $file_extension = 'error';
       return $false;
   }
   if ($file_extension eq 'ape' and $ape_path eq "") {
       $status_bar->push($context_id, " Cant choose ape format! mac not found.");
       pbar_erase_name();  # erase progress bar names
       $file_dialog->hide;
       $file_extension = 'error';
       return $false;
   }
   if ($file_extension eq 'flac' and $flac_path eq "") {
       $status_bar->push($context_id, " Cant choose flac format! flac not found.");
       pbar_erase_name();  # erase progress bar names
       $file_dialog->hide;
       $file_extension = 'error';
       return $false;
   }
   
   refresh_selection();    # refresh progress bar names
   make_array_of_files();  # make an array of files with extension *.mp3 or *.ogg or *.wav
   
   if ( $file_extension eq 'ogg'){     draw_input_output($da_arrow_dec,undef,("ogg","wav")); }
   elsif ( $file_extension eq 'mpc'){  draw_input_output($da_arrow_dec,undef,("mpc","wav")); }
   elsif ( $file_extension eq 'ape'){  draw_input_output($da_arrow_dec,undef,("ape","wav")); }
   elsif ( $file_extension eq 'flac'){ draw_input_output($da_arrow_dec,undef,("flac","wav"));}
   else { draw_input_output($da_arrow_dec,undef,("mp3","wav"));} # default
   if ( $file_extension eq 'wav'){
	$check_button_del_wav->set_active($false);  # default for this case
   }	
   if ( $file_extension eq 'mp3' or $file_extension eq 'mpc' or 
        ($file_extension eq 'ogg' and $vorbiscomment_path ne "") ) {
	
        $status_bar->push($context_id, " $file_extension file selected - info can be edited.");
   }   
   #$file_output = 'mp3'; # default output
   $file_dialog->hide;
}

# Connect the cancel_button to destroy the widget
$file_dialog->cancel_button->signal_connect( "clicked", sub { $file_dialog->hide; return $false; });

$file_dialog->show();

}
####--------------------------------------------####

sub refresh_selection {
   my $spinner_V_msg; 
   
   # get the last encode type from encode_choice()
   if ($button_output_mp3->get_active){$encode = $encode_remenber_mp3; $combo_const_avg->entry->set_text($encode);}
   if ($button_output_ogg->get_active){$encode = $encode_remenber_ogg; $combo_const_avg->entry->set_text($encode);}
   if ($button_output_mpc->get_active){$encode = $encode_remenber_mpc; $combo_const_avg->entry->set_text($encode);} 
   
   if ( $file_extension eq 'wav'){
          if ($pbar) {$pbar->set_text(" "); $pbar->set_fraction(0) ; }}
   elsif( $file_extension ne 'cda'){  
          if ($pbar) {$pbar->set_text(reduce_length_size($file_wav)); $pbar->set_fraction(0) ; } 
   }
   if ($pbar_n) {$pbar_n->set_text(reduce_length_size($file_wav)); $pbar_n->set_fraction(0) ;}
   
   if ($button_output_ogg->get_active) { 
       draw_input_output($da_arrow_enc,undef,("wav","ogg"));
       $frame21->set_label("oggenc settings");
       my $encode_copy = $combo_const_avg->entry->get_text(); # copy $encode value 
       if ( $encode_copy eq "constant" or $encode eq "constant" ){ 
            $encode = 'average';
            $encode_copy = 'average';}
       $combo_const_avg->set_popdown_strings( "average", "variable" );
       $combo_const_avg->entry->set_text($encode_copy); # necessary for not change the $encode value
       #print ("ogg: encode = $encode; encode_copy = $encode_copy\n");
       if ($pbar_lame) {$pbar_lame->set_text(reduce_length_size($file_ogg)); $pbar_lame->set_fraction(0) ;}
   }
   if ($button_output_mpc->get_active) { 
       draw_input_output($da_arrow_enc,undef,("wav","mpc"));
       $frame21->set_label("mpcenc settings");
       $combo_const_avg->set_popdown_strings( "variable" );
       $encode = $combo_const_avg->entry->get_text; 
       if ($pbar_lame) {$pbar_lame->set_text(reduce_length_size($file_mpc)); $pbar_lame->set_fraction(0) ;}
   }
   if ($button_output_mp3->get_active) { 
       draw_input_output($da_arrow_enc,undef,("wav","mp3"));  #default
       $frame21->set_label("lame settings");
       my $encode_copy = $combo_const_avg->entry->get_text(); # copy $encode value 
       #print ("antes -->encode_copy = $encode_copy \n");
       $combo_const_avg->set_popdown_strings( "average", "constant", "variable" );
       $combo_const_avg->entry->set_text($encode_copy); # necessary for not change the $encode value
       if ($pbar_lame) {$pbar_lame->set_text(reduce_length_size($file_mp3)); $pbar_lame->set_fraction(0) ;}
   }
   if ($button_output_ape->get_active) { 
       draw_input_output($da_arrow_enc,undef,("wav","ape"));
       $frame21->set_label("ape settings");
       if ($pbar_lame) {$pbar_lame->set_text(reduce_length_size($file_ape)); $pbar_lame->set_fraction(0) ;}
   }
   if ($button_output_flac->get_active) { 
       draw_input_output($da_arrow_enc,undef,("wav","flac"));
       $frame21->set_label("flac settings");
       if ($pbar_lame) {$pbar_lame->set_text(reduce_length_size($file_flac)); $pbar_lame->set_fraction(0) ;}
   }
   $status_bar->push($context_id, " ");   
   if ($button_output_mp3->get_active) { $file_output = 'mp3';}
   if ($button_output_ogg->get_active) { $file_output = 'ogg';}
   if ($button_output_mpc->get_active) { $file_output = 'mpc';}
   if ($button_output_ape->get_active) { $file_output = 'ape';}
   if ($button_output_flac->get_active){ $file_output = 'flac';}
   
   encode_choice_sensitive();
}

sub pbar_erase_name{
   if ($pbar) {$pbar->set_text(" "); $pbar->set_fraction(0) ; }
   if ($pbar_n) {$pbar_n->set_text(" "); $pbar_n->set_fraction(0) ;}
   if ($pbar_lame) {$pbar_lame->set_text(" "); $pbar_lame->set_fraction(0) ;}
}

#####------------------------------------------#####
# calcule the space to be reduced to obtain a standard stringsize
# we note that the char numbers are widthest than letters.
# substr string, pos, [n, replacement]
# Extracts and returns a substring n characters long, starting at character 
# position pos, from a given string.
sub reduce_length_size{
    my ($string) = @_;
    my $max_length = 48;
    my $length = length( $string );
    my $sub = $length - $max_length;
    #print("\n add = $add\n");
    if ($sub < 0 ){$sub = 0 ;}
    $string = substr($string,  $sub);
    #print("\n  string = $string\n");
    return $string;
}


#####----------=--vetor-com-arquivos--------------#####
#####----- array of mp3 or ogg or wav files ------#####

sub make_array_of_files {
        insert_msg("Files with \"$file_extension\" format in this directory:\n" , "small-black");
        @array_of_files = (); # empty , reset the array of files
        opendir(DIR, encode_char("$directory") ) or die "couldn't open dir $directory: $!";
	while (my $file = readdir(DIR)) {
		next unless $file =~ /\.$file_extension$/i;
		#/i option so that the search is case-insensitive
		#criando um vetor com os arquivos 
		push @array_of_files, $file; 
	}
	closedir(DIR);
        @array_of_files = sort @array_of_files;  #ordenando o vetor @vlegenda

        for (my $i=1;$i<=@array_of_files;$i++){  # @vlegenda  o tamanho do vetor @vlegenda
                # print on debug textview
                insert_msg("files($i) = $array_of_files[$i-1] \n", "small");
        }
	insert_msg("-----*****-----\n" , "small-black");
        return @array_of_files;
}

#####-------------- Programs Path -----------------#####

# sub from normalize
sub find_prog {
    my ($prog) = shift;
    my $comment = shift;
    my $path = "";
    my $fullpath;
    @_ = split(/:/, $ENV{PATH});
    for (@_) { 
        # $_ is an element of @_
	($_ .= "/") unless (/\/$/); # put a "/" at the end of path
	# print " S = $_ \n";
	$fullpath = $_.$prog;
	if (-x $fullpath) { # if (-x file) : file is executable by effective uid/gid.
	    $path = $fullpath;
	    last; #abandon the loop
	}
    }
    if ( not defined($comment) ) { $comment = "";}
    if ($path eq "") {
       insert_msg("Cant find \"" , "black");
       insert_msg("$prog" , "red");
       insert_msg("\" in executable path. $comment\n" , "black");
    }
    return $path;
}

sub verify_all_paths {  
    $lame_path = find_prog("lame", "The output cant be \"mp3\" format.");
    #print " lame_path = $lame_path \n";
    if ($lame_path eq "") { $button_output_mp3->set_sensitive($false); }
    
    $oggenc_path = find_prog("oggenc", "The output cant be \"ogg\" format.");
    #print " oggenc_path = $oggenc_path \n";
    if ($oggenc_path eq "") { $button_output_ogg->set_sensitive($false); }
    
    $mpcenc_path = find_prog("mppenc", "The output cant be \"mpc\" format.");
    if ($mpcenc_path eq "") { $button_output_mpc->set_sensitive($false);}
    
    $ape_path = find_prog("mac", "The output cant be \"ape\" format.");
    if ($ape_path eq "") { $button_output_ape->set_sensitive($false);  }
    
    $flac_path = find_prog("flac", "The output cant be \"flac\" format.");
    if ($flac_path eq "") { $button_output_flac->set_sensitive($false);}
    
    if ($lame_path ne "") {$button_output_mp3->set_active($true);}
    elsif ($oggenc_path ne "") {$button_output_ogg->set_active($true); }
    elsif ($mpcenc_path ne "") {$button_output_mpc->set_active($true); }
    elsif ($ape_path ne ""   ) {$button_output_ape->set_active($true); }
    elsif ($flac_path ne ""  ) {$button_output_flac->set_active($true);}
    else {$normalize_button->set_sensitive($false);} # do nothing!!
    
    $metaflac_path = find_prog("metaflac");
    
    $normalize_path = find_prog("normalize", "The wave files cant be normalized.");
    if ($normalize_path eq "") {
       $type = "None";
       $opt_menu->set_history (3); # 3 set to "None"
       $box_type->set_sensitive($false);
       $spinner->set_sensitive($false);
    }
    $oggdec_path = find_prog("oggdec");
    
    $mpcdec_path = find_prog("mppdec");
    
    $cdparanoia_path = find_prog("cdparanoia");
    if ($cdparanoia_path eq "") {
	$combo_rip->entry->set_text("cdda2wav"); 
	$combo_rip->set_sensitive($false);
	$ripper = "cdda2wav";
    } 
    $cdda2wav_path = find_prog("cdda2wav");
    if ($cdda2wav_path eq "") {
	$combo_rip->entry->set_text("cdparanoia"); 
	$combo_rip->set_sensitive($false);
	$ripper = "cdparanoia";
    }  
    
    $ogginfo_path = find_prog("ogginfo");
    
    $vorbiscomment_path = find_prog("vorbiscomment");
    
    #cdplayer
    $cdcd_path = find_prog("cdcd", "cdcd is a Command Driven CD player.");
    if ( $cdcd_path ne "" and not -e encode_char("$home/.cdcdrc") ){ 	
       insert_msg("\nFirst run \"cdcd\" in command line to make the config file .cdcdrc\n" , "black");
    } 

    # libcdaudio  ;; Audio::CD ;; see <man Audio::CD>
    if ( $use_audiocd eq $false and $cdcd_path eq "" ){ 	
	if ($use_audiocd eq $false) {
           insert_msg("Cant use the perl module \"" , "black");
           insert_msg("Audio::CD" , "red");
           insert_msg("\".\n" , "black");
        }	
        $cdplay_path = find_prog("cdplay", "Cant play audio CD."); 
    } 
    else {  $cdplay_path = ""; }
       
}
verify_all_paths();

#####----------- get and show mp3/ogg info --------#####

sub get_info_from_file {

   #my $fh = encode("utf8",$file_mp3);
   #print ("fh = $fh\n");
   #my $fh2 = decode("utf8",$fh);
   #print ("fh2 = $fh2\n");
   
   # erase the global strings tags of files
   $mp3_title=""; $mp3_artist=""; $mp3_album=""; $mp3_year="";
   $mp3_comment=""; $mp3_tn=""; $mp3_genre=""; $mp3_freq = 0;
   $mode_original = ""; $mp3_time = ""; $mp3_mpeg = "MPEG";
   $bitrate_average = $true; $total_track = "";
   $bitrate_original = 160; # standard value if "change properties" is not active 
   #$encode = $combo_const_avg->entry->get_text(); #refresh $encode value
   
   #------------------------mp3--------------------------#
   # Only show the mp3 info for a file whose extension is .mp3
   if ( $file_extension eq 'mp3' ){
      # get_mp3_info_from_file(); 
      #read_ID3v1_mp3_tag($file_mp3);      
      #$bitrate_average = read_Xing_mp3_tag($file_mp3);
      read_mp3_info_tag($file_mp3);
      # show_mp3_info2($file_mp3);
   }    
   
   #------------------------ogg--------------------------#
   # Only show the ogg info for a file whose extension is .ogg
   if ( $file_extension eq 'ogg' ){
      get_ogg_info_from_file();
   } 
   
   #------------------------mpc--------------------------#
   # Only show the ogg info for a file whose extension is .mpc
   if ( $file_extension eq 'mpc' ){    
      show_mpc_info($file_mpc);   
      show_mpc_info2($file_mpc);
      $mp3_freq = sprintf("%0.0f",$mp3_freq/1000); 
      $encode = 'average';
      $bitrate_average = $true;
   }   
   #------------------------ape--------------------------#
   # Only show the ogg info for a file whose extension is .ape
   if ( $file_extension eq 'ape' ){     
      show_mpc_info($file_ape);     
      $mp3_freq = 44;
      $mp3_mpeg = "APE"; 
      $encode = 'average';
      $bitrate_average = $true;
   }  
   #------------------------flac-------------------------#
   # Only show the meta info for a file whose extension is .flac
   if ( $file_extension eq 'flac' ){
      get_flac_info_from_file();      
   } 
   
   #-------------- set the entries of file --------------#
    
   $entry_title->set_text( "$mp3_title" );
   $entry_artist->set_text( "$mp3_artist" );
   $entry_album->set_text( "$mp3_album" ); 
   $entry_year->set_text( "$mp3_year" ); 
   #track number of mp3   
   $entry_tn->set_text( "$mp3_tn" );
   $entry_tt->set_text( "$total_track" );   
   $entry_comment->set_text( "$mp3_comment" );  
   $combo_genre->entry->set_text( "$mp3_genre" );
   $label_mpeg->set_label ("$mp3_mpeg");
   if ($mp3_freq == 0){ $label_freq->set_label ("Frequency:");}
   else { $label_freq->set_label ("Frequency: $mp3_freq KHz");}
   $label_time->set_label ("Time: $mp3_time"); 
   
   if ( $file_extension eq 'ogg' and $ogginfo_path ne ""){ 
          $label_mode->set_label ($channel);
	  $label_kbps->set_label ("Average: $bitrate_original kb/s"); 
   }
   elsif ( $file_extension eq 'flac' and $metaflac_path ne ""){ 
          $label_mode->set_label ($channel);
	  $label_kbps->set_label ("Bitrate: "); 
   }
   elsif ( $file_extension eq 'mpc' ){ 
	  $label_kbps->set_label ("Bitrate: $bitrate_original kb/s"); 
	  $label_mode->set_label ("Channels: 2");
   }
   else { $label_mode->set_label ("Mode: $mode_original");}
   if ( $file_extension =~ /(wav|cda|ape)/ ){ 
        $label_kbps->set_label ("Bitrate:");
	$mp3_freq = 44.1;
   } 
   
   $button_save_tag->set_sensitive($false); # always when a new file is loaded.  
   #show_mp3_info2($file_mp3);
}


# see normalize-mp3, perl-MP3-Tag-0.92-1mdk.noarch.rpm, 
# perl-MP3-ID3v1Tag-1.11-5mdk.noarch.rpm - <man MP3::Tag>
# see /usr/lib/perl5/vendor_perl/5.8.5/MP3/Tag

# only for ilustration - not used
sub show_mp3_info2 {
    my ($fname) = @_;
    my ($retval, $id3v1_tag, $id3v2_tag, $id3v2_sz);
    
    my $file = $directory."/".$fname;

    if ($fname =~ /\.mp3$/i) {
	open(IN, encode_char("$file") ) || die "Can't read $fname: $!, stopped";
	# read ID3v2 tag, if it's there
	# FIXME: doesn't work for ID3v2.4.0 appended tags
	
	# read (filehandle, $var, length [,offset]);
	# "Attempts to read length bytes of data into variable $var from the specified filehandle.
	# The offset, if specified, says where in the variable $var to start putting bytes, 
	# so that you can do a read into the middle of a string."
	
	# unpack ("template", string);
        # "Takes a string representing a data structure and expands it into a list value, 
	# returning the list value. (unpack does the reverse of pack.) In a scalar context, 
	# it can be used to unpack a single value. The template ... specifies the order and 
	# type of the values to be unpacked. "
	    
	# pack ("template", list);
        # "Takes a list of values and packs it into a binary structure, returning the string 
	# containing the structure. The template is a sequence of characters that give the 
	# order and type of values, as follows:
	# C : An unsigned char value
	# x : A null byte
	# Each character may optionally be followed by a number that gives a repeat count.
	# Field specifiers may be separated by whitespace, which will be ignored."
	# Perl in a Nutshell - OReilly - By Ellen Siever, Stephen Spainhour & Nathan Patwardhan
	
	# This i a example of content of mp3 file:
        # ID3    {TSSE      LAME v3.96COMM 
	#   !   XXXID3v1 Comment Created by GripTIT2    
	#   Vinheta do CapetaTPE1      Almir SaterTALB  
	#  
   InstrumentalCOMM      XXX Created by 
	# GripTYER      1885TRCK      3TCON 
	#      (0)
	
	read(IN, $id3v2_tag, 3); # read the firt 3 byte
	if ($id3v2_tag eq "ID3") {
	    read(IN, $id3v2_tag, 7, 3);	
	    print (" --> id3v2_tag = $id3v2_tag \n");    
	    # figure tag size
	    my ($x1, $x2, $x3, $x4) = unpack("x6 C*", $id3v2_tag);
	    my $tagsz = $x1; 
	    $tagsz <<= 7;  # << appending 7 null byte. 
	    $tagsz += $x2;
	    $tagsz <<= 7;
	    $tagsz += $x3;
	    $tagsz <<= 7;
	    $tagsz += $x4;
	    read(IN, $id3v2_tag, $tagsz, 10); # 10 = 7 + 3 of read(IN, $id3v2_tag, 7, 3);
	    $id3v2_sz = $tagsz + 10;  # size of id3v2
	} else {
	    undef $id3v2_tag;
	    $id3v2_sz = 0;
	}
	# read ID3v1 tag, if it's there
	seek(IN, -128, 2);
	read(IN, $id3v1_tag, 3);
	if ($id3v1_tag eq "TAG") {
	    read(IN, $id3v1_tag, 125, 3);
	} else {
	    undef $id3v1_tag;
	}
	close(IN);
	print (" id3v1_tag = $id3v1_tag \n");
        print (" id3v2_tag = $id3v2_tag \n");
        print (" id3v2_sz = $id3v2_sz \n");

	$retval = [ 'id3', $id3v1_tag, $id3v2_tag, $id3v2_sz ];
    } else {
	$retval = [ 'none' ];
    }
    $retval;
}

sub show_mpc_info {
    my $fname = shift;
    my $APEtag;
    my $file_path = $directory."/".$fname; 

    if ($file_path =~ /\.(mpc|ape)$/i) {
	open(IN, encode_char("$file_path") ) || die "Can't read $file_path: $!, stopped";

	# read APETAGEX, if it's there
	# split /PATTERN/,EXPR,LIMIT
	# seek FILEHANDLE,POSITION,WHENCE
	seek(IN,-800, 2);  # read 800 byte from the end of file	
	read(IN, $APEtag, 800, 0); 
	if ( $APEtag !~ /APETAGEX/is ){ return; }

	(undef,$APEtag) = split /APETAGEX/,$APEtag ;

	close(IN);  # Year1975 Track07
    }
    else{ return;}
    
    my $version = substr($APEtag,0,4);
    $version = unpack "L",$version;
    #print "version = $version\n";
    $APEtag = substr($APEtag,4);
    
    my $tag_size = substr($APEtag,0,4);
    $tag_size = unpack "L",$tag_size;
    #print "tag_size = $tag_size\n";
    $APEtag = substr($APEtag,4);
    
    my $item_count = substr($APEtag,0,4);
    $item_count = unpack "L",$item_count;
    #print "item_count = $item_count\n";
    $APEtag = substr($APEtag,4);
    
    my $tag_flags = substr($APEtag,0,4);
    $tag_flags = unpack "%32b*",$tag_flags;
    #print "tag_flags = $tag_flags\n";
    $APEtag = substr($APEtag,12);
    
#/===========================================================================\
#| Preamble       | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' }     |
#|----------------|---------|------------------------------------------------|
#| Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000     |
#|----------------|---------|------------------------------------------------|
#| Tag Size       | 4 bytes | Tag size in bytes including footer and all tag |
#|                |         | items excluding the header (for 1.000          |
#|                |         | compatibility)                                 |
#|----------------|---------|------------------------------------------------|
#| Item Count     | 4 bytes | Number of items in the tag                     |
#|----------------|---------|------------------------------------------------|
#| Tag Flags      | 4 bytes | Global flags                                   |
#|----------------|---------|------------------------------------------------|
#| Reserved       | 8 bytes | Must be zeroed                                 |
#\===========================================================================/  

#                          Item:  length key value
# APE Tag Item:   4 byte : Length len of the assigned value in bytes
#	    	  4 byte : Item flags
#	    	  m byte : Item key, can contain ASCII characters from 0x20 (Space) up to 0x7E (Tilde)
#	    	  1 byte : Item key terminator   0x00
#	    	  len byte : Item value, can be binary data or UTF-8 string 

    my $tagLen;
    my ($tag_key, $tag_val);
    my %taghash = ();
    my $tag_copy = $APEtag; 

    for (my $i=0;$i<$item_count;$i++){
        $tagLen = substr($APEtag,0,4);
        $APEtag = substr($APEtag,8);
        $tagLen = unpack "L",$tagLen;
	#print "tagLen = $tagLen ;; APEtag = $APEtag\n";
	
	if ($APEtag =~ /^(.*?)\0/) { $tag_key = $1; }  # \0 =  
	#$tag_key =~ tr/A-Z/a-z/;
        $APEtag =~ s/^.*?\0//;

        $tag_val = substr $APEtag, 0, $tagLen; # read
	$tag_val = decode("utf8",$tag_val);	
        $APEtag  = substr $APEtag, $tagLen;    # cut, remove

	#print "tag_key = $tag_key ;; tag_val = $tag_val \n"; 
	# Fill the hash
	$taghash{$tag_key} = $tag_val;	
    } 
    #print "Album = ",$taghash{Album},"\n\n";
    #for my $key (keys %taghash) { print "$key => $taghash{$key}\n"; }
    
    if ( defined($taghash{Year})    ){ $mp3_year = $taghash{Year}; }
    if ( defined($taghash{Artist})  ){ $mp3_artist = $taghash{Artist}; }
    if ( defined($taghash{Title})   ){ $mp3_title = $taghash{Title}; }
    if ( defined($taghash{Comment}) ){ $mp3_comment = $taghash{Comment}; }
    if ( defined($taghash{Genre})   ){ $mp3_genre = $taghash{Genre}; }
    if ( defined($taghash{Album})   ){ $mp3_album = $taghash{Album}; }
    if ( defined($taghash{Track})   ){ $mp3_tn = $taghash{Track}; }
    
    # To read again    
    # print ("\n--> APEtag = $APEtag \n");
    # [^ ]*  all character but <null>     
    if( $mp3_year eq "" and $tag_copy =~ /(date|year) ([^ ]*)(. {7}|$)/is ){ $mp3_year = $2 ; }    
    if( $mp3_album eq "" and $tag_copy =~ /album ([^ ]*)(. {7}|$)/is ){ $mp3_album = decode("utf8",$1) ; }    
    if( $mp3_artist eq "" and $tag_copy =~ /artist ([^ ]*)(. {7}|$)/is ){ $mp3_artist = decode("utf8",$1) ; }   
    if( $mp3_title eq "" and $tag_copy =~ /Title ([^ ]*)(. {7}|$)/is ){ $mp3_title = decode("utf8",$1) ; }    
    if( $mp3_comment eq "" and $tag_copy =~ /Comment ([^ ]*)(. {7}|$)/is ){ $mp3_comment = decode("utf8",$1) ; }   
    if( $mp3_genre eq "" and $tag_copy =~ /Genre ([^ ]*)(. {7}|$)/is ){ $mp3_genre = decode("utf8",$1) ; }   
    if( $mp3_tn eq "" and $tag_copy =~ /Track ([^ ]*)(. {7}|$)/is ){ $mp3_tn = $1 ; }

    if( $mp3_tn =~ /(\d+)\/(\d+)/ ){ $mp3_tn = $1 ; $total_track = $2 ; }
    #print ("--> track = $mp3_tn ;; total_track = $total_track \n");
}


sub show_mpc_info2 {
    my $fname = shift;
    my $header;
    my $file_path = $directory."/".$fname; 
    my $Streamversion; 
    my $buffer;
    
    use constant MPCHEADERFLAG => 'MP+';
    
    my @profileNames = (
		'na', "Unstable/Experimental", 'na', 'na',
		'na', "below Telephone", "below Telephone", "Telephone",
		"Thumb", "Radio", "Standard", "Xtreme",
		"Insane", "BrainDead", "above BrainDead", "above BrainDead"
    );

    my @samplFreq = qw(44100 48000 37800 32000);

    if ($file_path !~ /\.mpc$/i) { return $false;}
    
    open(IN, encode_char("$file_path") ) || die "Can't read $file_path: $!, stopped";
    
    binmode IN;
    my $fileSize   = -s encode_char("$file_path");
    
    # Read musepack header.
    read (IN, $header, 3); # 3 byte for 'MP+'

    if ($header ne MPCHEADERFLAG) { return $false; }
    
    # read one byte
    read IN, $Streamversion, 1;    
    $Streamversion = unpack "C", $Streamversion;
    #print "Streamversion = $Streamversion\n";
    if ( $Streamversion < 7 ){ return; } # Musepack SV version supported
    
    # read 4 byte
    read IN, $buffer, 4;    
    my $frame_count = unpack "L", $buffer;
    #print "frame_count = $frame_count\n";
    
    read IN, $buffer, 4; # for Stream Flags 
    
    my $StreamFlags = unpack "L", $buffer;
    #print "StreamFlags = $StreamFlags\n";   # 4 byte = 32 bit
    $StreamFlags = sprintf( "%032b", $StreamFlags ); # convert $StreamFlags to binary base with 32 digits
    #print "StreamFlags = $StreamFlags\n";  
    
    my $profile = bin2dec(substr($StreamFlags, 8, 4)); # get the profile number
    #print "profile = $profile\n";    
    $profile = $profileNames[$profile];
    $mp3_freq = $samplFreq[bin2dec(substr($StreamFlags, 14, 2))];
    
    read IN, $buffer, 12;
        
    $buffer = unpack "L", substr($buffer,8);  # remove 8 byte, remainde 4 byte
    $buffer = sprintf( "%032b", $buffer ); # convert $StreamFlags to binary base with 32 digits   
    
    my $lastValidSamples = bin2dec(substr($buffer, 1, 11));
    #print "profile = $profile ;; samplFreq = $mp3_freq ;; lastValidSamples = $lastValidSamples \n";
        
    my $totalSamples = ($frame_count - 1)*32*36 + $lastValidSamples;
    my $totalSeconds = $totalSamples/$mp3_freq;
    $totalSeconds = sprintf("%0.0f",$totalSeconds);
    
    # convert x seconds to hour:min:sec format
    my ($hour,$min,$sec) = sec_to_time($totalSeconds);    # return ($hour,$min,$sec);
    
    my $bitrate = 8 * $fileSize / $totalSeconds; # 8*$filesize = size in bit
    $bitrate = sprintf("%0.0f",$bitrate/1000);
    #print "Kbitrate = $bitrate ;; time = $hour:$min:$sec ;; totalSeconds = $totalSeconds\n"; 

    close(IN);
    
    $Streamversion = sprintf("%0.1f",$Streamversion);
    $mp3_mpeg = "MPC, SV: $Streamversion\nProfile: '$profile'";  
    $mp3_time = $hour > 0 ? "$hour:$min:$sec" : "$min:$sec" ;
    $bitrate_original = $bitrate;
    
    # SV 8.0 Header - Item    Size ( 1 byte = 8 bit )
    # Preample                  3 byte 'MP+'
    # Streamversion             1 byte
    # TotalLength of header     4 byte
    # Stream Flags              4 byte
    # Subband width*32Hz        2 byte
    # Num. of max subbands      6 bit
    # N. of subbands using IS   5 bit
    # -                         2 bit
    # Musepack Frames           4 byte
    # -                         2 bit
    # PCM samples per Channel   6 byte
    # -                         2 byte
    # Filelength of Audio frame 6 byte
    # -                         2 byte
    # Total file size           6 byte
    # ...
          
    
    # tell    Returns the current position in bytes for FILEHANDLE,
    # sysread FILEHANDLE,SCALAR,LENGTH,OFFSET  ;; Returns the number of bytes actually read
    # syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET  
    # substr EXPR,OFFSET,LENGTH,REPLACEMENT  ;; Extracts a substring out of EXPR and returns it.
}

sub read_ID3v1_mp3_tag {
    my $fname = shift;
    my $file_path = $directory."/".$fname; 
    my $buffer;
    
    my ($title, $artist, $album, $year, $comment, $tracknum, $genre);
    my @info = ();
    

    if ($file_path !~ /\.mp3$/i) { return $false;}
    
    open(IN, encode_char("$file_path") ) || die "Can't read $file_path: $!, stopped";
    
    binmode IN;
    my $fileSize   = -s encode_char("$file_path");
    
    
    seek(IN,-128, 2);  # Set FileHandle 128 byte from the end of file 
    read(IN, $buffer, 128, 0); # read 128 byte to $buffer
	  
    my $preamble = substr($buffer,0,3); # read 3 byte 
    return unless $preamble eq 'TAG';
    $buffer = substr($buffer, 3);  # remove 3 byte
    
    $tracknum = unpack "C", substr($buffer, 123, 1);
    
    # get TAG to variables
    if ( defined($tracknum) ){
           ( $title, $artist, $album, $year, $comment, $tracknum, $genre ) = unpack "a30a30a30a4a28xCC", $buffer;}
    else { ( $title, $artist, $album, $year, $comment, $genre )            = unpack "a30a30a30a4a30C", $buffer;
             $tracknum = "";
    }    
    print "title = $title ;; artist = $artist ;; album = $album ;; year = $year  ;; comment = $comment \n"; 
    print "track number = $tracknum ;; genre = $genre ;; fileSize = $fileSize\n";  
    return ($title, $artist, $album, $year, $comment, $tracknum, $genre);   
}

sub save_ID3v1_mp3_tag { 
    my ( $file, $title, $artist, $album, $year, $comment, $tracknum, $genre) = @_;        
    my $buffer;
    
    open(IN, encode_char(">>$file") ) || die "Can't read $file: $!, stopped";
    
    binmode IN;        
    seek(IN,-128, 2);          # Set FileHandle 128 byte from the end of file 
    read(IN, $buffer, 128, 0); # read 128 byte to $buffer
	  
    my $preamble = substr($buffer,0,3); # read 3 byte    
    if ( $preamble ne 'TAG' ){ seek(IN, 0, 2); } # go to the end of file  
    
    # save TAG to file, overwrite if it exist
    if ( $tracknum =~ /^\d+$/ ) {
                $tracknum = $tracknum > 255 ? 255 : $tracknum; # must be an integer from 1 and 255
		print IN pack "a3a30a30a30a4a28xCC", 'TAG', $title, $artist, $album, $year, $comment, $tracknum, $genre; }
    else {
		print IN pack "a3a30a30a30a4a30C", 'TAG', $title, $artist, $album, $year, $comment, $genre;
    } 
    close(IN);
}


sub read_Xing_mp3_tag {
    my $fname = shift;
    my $file_path = $directory."/".$fname;  
    my $buffer;
    my $off = -1; my $byte = "" ; my $try_harder = 4096;
    my $frames = 0;  my $fileSize = 0; 

    if ($file_path !~ /\.mp3$/i) { return $false;}
    
    open(IN, encode_char("$file_path") ) || die "Can't read $file_path: $!, stopped";
    
    binmode IN;  
    
    # check first three bytes for 'ID3'
    seek IN, 0, 0;
    read IN, $buffer, 3;
    
    if ($buffer eq 'ID3'){    
       # get ID3v2 tag length from bytes 7-10
       my $tag_size = 10;	# include ID3v2 header size
       seek IN, 6, 0;
       read IN, $buffer, 4;
       $tag_size += ID3_GET_SIZE28($buffer);  
       print "tagsize = $tag_size\n";       
       $off = $tag_size;
    }   
    # For Xing headers, see "http://home.pcisys.net/~melanson/codecs/mp3extensions.txt"
    # Xing headers are often added to VBR (variable bit rate) MP3   

    my $myseek = sub {
		my $n = $_[0] || 4;  # read 4 bytes for one loop
		$off += $_[1] || 1;  # advance 1 byte for one loop
		seek IN, $off, 0;
		read IN, $byte, $n;
    };
    while ($byte ne 'Xing') {
		&$myseek;
		if ($off > $try_harder) { return $false; }
		#print "byte = $byte ;; off = $off\n";
    }    	    
    print "preamble_xing = $byte ;; off = $off\n";
        
    &$myseek(4,4);
    my $flags = unpack_head($byte);
    
    if ($flags & 1) {
		&$myseek(4,4);
		$frames = unpack_head($byte);
    }
    if ($flags & 2) {
		&$myseek(4,4);
		$fileSize = unpack_head($byte);
    }
    
    print "frames = $frames ;; fileSize = $fileSize\n";
    $mp3_freq = 48000;
    
    my $timePerFrame = 32*36;  #32*36 = 1152
    my $totalSamples = $frames * $timePerFrame;
    my $totalSeconds = $totalSamples/$mp3_freq;
    my $bitrate = 8 * $fileSize / $totalSeconds; # 8*$filesize = size in bit
    
    $totalSeconds = sprintf("%0.0f",$totalSeconds);    
    # convert x seconds to hour:min:sec format
    my ($hour,$min,$sec) = sec_to_time($totalSeconds);    # return ($hour,$min,$sec);    
    
    $bitrate = sprintf("%0.1f",$bitrate/1000);
    print "Kbitrate = $bitrate ;; time = $hour:$min:$sec ;; totalSeconds = $totalSeconds\n";           
    
   return $true;   
}

# from MPEG::MP3Info
sub unpack_head { unpack('l', pack('L', unpack('N', $_[0]))); }


# from MP3::Info
sub read_mp3_info_tag {
    my $fname = shift;
    my $file_path = $directory."/".$fname;  

    if ($file_path !~ /\.mp3$/i) { return $false;}
    
    my $file = encode_char("$file_path");
    
    my $mp3 = MP3::Info->new($file); ## MP3::Info - Copyright (c) 1998-2003 Chris Nandor.

    my @channel_mode = ('stereo','joint stereo','dual channel','single channel');    
                  
    MP3::Info::get_mp3tag($file); 
    MP3::Info::get_mp3info ($file);
    
    $mp3_title = $mp3->title;     if (not defined($mp3_title)){$mp3_title = "";}    
    $mp3_artist = $mp3->artist;   if (not defined($mp3_artist)){$mp3_artist = "";}   
    $mp3_album = $mp3->album;     if (not defined($mp3_album)){$mp3_album = "";}   
    $mp3_year = $mp3->year;       if (not defined($mp3_year)){$mp3_year = "";}   
    $mp3_comment = $mp3->comment; if (not defined($mp3_comment)){$mp3_comment = "";}   
    $mp3_genre = $mp3->genre;     if (not defined($mp3_genre)){$mp3_genre = "";}   
       
    #($mp3_title, $mp3_artist, $mp3_album, $mp3_year, $mp3_comment, $mp3_tn, $mp3_genre) = read_ID3v1_mp3_tag($file_mp3);
    
    # if $mp3_genre is a number, get its reference name
    if ( $mp3_genre =~ /^\d+$/ ){ $mp3_genre = $genre_hash{"$mp3_genre"}; }  # $genre_hash{"32"}
    
    $mp3_tn = $mp3->tracknum; if (not defined($mp3_tn)){$mp3_tn = ""}   
    if( $mp3_tn =~ /(\d+)\/(\d+)/ ){ $mp3_tn = $1 ; $total_track = $2 ; }
    
    $bitrate_average = $mp3->vbr eq $true? $true: $false;
    $mp3_freq = $mp3->frequency;
    $mode_original = $channel_mode[$mp3->mode];
    $mp3_time = $mp3->time;
    $bitrate_original = $mp3->bitrate;
    
    if ($bitrate_average){
          $label_kbps->set_label ("Average: $bitrate_original kb/s");}
    else{ $label_kbps->set_label ("Bitrate: $bitrate_original kb/s");}  
    
    $mp3_mpeg = "MPEG".$mp3->version."/layer".$mp3->layer;     

}

# only obtain info from mp3 files
sub get_mp3_info_from_file2 {  # No more used. Now MP3::Info/save_tag() are used to obtain/save mp3 informations
   my $mp3info_path = 'mp3info';
   my $id3info_path = 'id3info';
   
   if ($mp3info_path eq "" or $id3info_path eq "") {
       insert_msg("None informations about \"mp3\" format because cant find \"" , "black");
       insert_msg("mp3info" , "red"); insert_msg("\" or \"" , "black");
       insert_msg("id3info" , "red");
       insert_msg("\" in executable path.\n\n" , "black");
       return $false;
   }
   
   my $cmd; # command

   $cmd = "$mp3info_path -x \"$directory/$file_mp3\" ";
	    
   my $res = open (INFO, encode("$character"," $cmd 2>&1 |") )  or die;
   
   ## pid process of mp3info 
   if( !defined($res) ) {
	    $status_bar->push($context_id, " Could not find mp3info!");
	    return $false;
   }
   
   $/="\r"; ## start of loop   
   while(<INFO>){ # id3tag v1
     
	    #File: almir_sater-instrumental-03_vinheta_do_capeta.mp3
            #Title:   Vinheta do Capeta              Track: 3
            #Artist:  Almir Sater
            #Album:   Instrumental                   Year:  1985
            #Comment: Created by Grip                Genre: Folk [80]
            #Media Type:  MPEG 1.0 Layer III
            #Audio:       Variable KB/s, 44KHz (joint stereo)
	    #Length:      5:30

            # discover if the bitrate of mp3 file is variable
            if( $_ =~ /Audio:\s+Variable.*/ ){
		   $bitrate_average = $true; }
	    else{  $bitrate_average = $false;}  

            if( $_ =~ /.*Genre:\s+(.*)\s\[/i ){ $mp3_genre = $1 ; }
	    if( $_ =~ /Audio:.*\s+(\d+)KHz.*\n/i ){ $mp3_freq = $1; }
	    if( $_ =~ /Audio:.*KHz\s+\((.*)\).*\n/i ){ $mode_original = $1 ; }
	    if( $_ =~ /Length:\s+(.*)/i ){ $mp3_time = $1 ; }
	    # print ("mp3 freq = $mp3_freq \n");
   }
   $/="\n"; ## final of loop
   close(INFO);
   
    #example of output of  <id3info   some_music.mp3>
   
    #*** Tag information for /tmp/01novos/almir_sater-instrumental-03_vinheta_do_capeta.mp3
    #=== TSSE (Software/Hardware and settings used for encoding): LAME v3.96
    #=== TIT2 (Title/songname/content description): Vinheta do Capeta
    #=== TPE1 (Lead performer(s)/Soloist(s)): Almir Sater
    #=== TALB (Album/Movie/Show title): Instrumental
    #=== TYER (Year): 1885
    #=== TRCK (Track number/Position in set): 3
    #=== TCON (Content type): (80)
    #=== COMM (Comments): (ID3v1 Comment)[XXX]: Created by Grip
    #*** mp3 info
    #MPEG1/layer III
    #Bitrate: 128KBps
    #Frequency: 44KHz
     
   $cmd = "$id3info_path \"$directory/$file_mp3\" ";
   #print ("cmd = $cmd\n");  
   
   open  (G, encode_char("$cmd 2>&1 |") )  or die;
   $/="\r"; ## start of loop
   while(<G>){  # id3tag v2
        if( $_ =~ /.*TIT2.*\):\s(.*)\n/ ){ $mp3_title = $1 ; } 
	if( $_ =~ /.*TPE1.*\):\s(.*)\n/ ){ $mp3_artist = $1 ; } 
	if( $_ =~ /.*TALB.*\):\s(.*)\n/ ){ $mp3_album = $1 ; } 
	if( $_ =~ /.*TYER.*\):\s(.*)\n/ ){ $mp3_year = $1 ; }
	if( $_ =~ /.*TRCK.*\):\s(\d+)\/?(\d+)?\n/ ){ $mp3_tn = $1 ; }
	if( $_ =~ /.*TRCK.*\):\s(\d+)\/(\d+)\n/ ){ $total_track = $2; }
	if( $_ =~ /.*COMM.*\]:\s(.*)\n/ ){ $mp3_comment = $1 ; }
	if( $mp3_genre eq "" ){
	    if( $_ =~ /.*TCON.*\):\s+\((\d+)\).*\n/ ){ $mp3_genre = $1; } # $1 is a number
	    elsif( $_ =~ /.*TCON.*\):\s(.*)\n/ ){ $mp3_genre = $1 ; }
	}
	if( $_ =~ /(MPEG.*)/ ){ $mp3_mpeg = $1 ; }
	# print ("total_track = $total_track \n");
   }
   $/="\n"; ## final of loop
   close(G);   
   
   #print "--> mp3_genre = $mp3_genre   \n ";
   # if $mp3_genre is a number, get its reference name
   if ( $mp3_genre =~ /^\d+$/ ){  
      $mp3_genre = $genre_hash{"$mp3_genre"}; 
      #print " mp3_genre = $mp3_genre   \n ";
   }  # $genre_hash{"32"}
   
   # calculate the bitrate of file loaded and save it on $bitrate_original
   if ( -e encode_char("$directory/$file_mp3") ) {  
      $cmd = "$mp3info_path -r a -p \"%.0r\" \"/$directory/$file_mp3\" ";
      $bitrate_original=exec_cmd_system($cmd);
   }
   else { $file_extension = "other"; 
          return;
   }
   
   if ($bitrate_average){
         $label_kbps->set_label ("Average: $bitrate_original kb/s");}
   else{ $label_kbps->set_label ("Bitrate: $bitrate_original kb/s");}      
} 


# only obtain info from ogg files
sub get_ogg_info_from_file {

   if ($ogginfo_path eq "") {
       insert_msg("None informations about \"ogg\" format because cant find \"" , "black");
       insert_msg("ogginfo" , "red");
       insert_msg("\" in executable path.\n\n" , "black");
       return $false;
   }
   
   my $cmd; # command

   $cmd = "$ogginfo_path -v \"$directory/$file_ogg\" ";
	    
   my $res = open (OGGINFO, encode("$character","$cmd 2>&1 |") )  or die;
   
   ## pid process of ogginfo 
   if( !defined($res) ) {
	    $status_bar->push($context_id, " Could not find ogginfo!");
	    return $false;
   }
   
   $/="\r"; ## start of loop   
   while(<OGGINFO>){ 
               
            #New logical stream (#1, serial: 424f22ae): type vorbis
            #Vorbis headers parsed for stream 1, information follows...
            #Version: 0
            #Vendor: Xiph.Org libVorbis I 20030909 (1.0.1)
            #Channels: 2
            #Rate: 44100

            #Nominal bitrate: 112.001000 kb/s        or  Nominal bitrate: 112,001000 kb/s
            #Upper bitrate: 0.320000 kb/s
            #Lower bitrate: 0.064000 kb/s
            #User comments section follows...
            #        comment=gnormalize Fix value=30
            #        title=Vinheta do Capeta
            #        artist=Almir Sater
            #        genre=Folk
            #        date=1885
            #        album=Instrumental
            #        tracknumber=3
            #Vorbis stream 1:
            #        Total data length: 814983 bytes
            #        Playback length: 0m:56s
            #        Average bitrate: 115.015600 kbps
            #Logical stream 1 ended

            # discover if the bitrate of mp3 file is variable 

            if( $_ =~ /Channel(.*)\n/i ){ $channel = "Channel$1";}
	    if( $_ =~ /Rate:\s(.*)\n/i ){ 
	              my $fq = sprintf("%0.0f", $1/1000 );  
		      $mp3_freq = $fq; }
	    if( $_ =~ /Nominal\sbitrate:\s+(\d+[\.,]\d+)\s.*\n/i ){ 
		      my $br = sprintf("%0.0f",$1);
		      $br = number_value($br);
		      #print (" br = $br - 1 = $1\n");
		      $bitrate_original = $br; }
	    if( $_ =~ /\s+Playback\slength:\s+(.*)\n/i ){ $mp3_time = $1 ; }
	    if( $_ =~ /\s+genre=(.*)\n/i ) { $mp3_genre = $1 ;  }
	    if( $_ =~ /\s+title=(.*)\n/i ) { $mp3_title = $1 ;  } 
	    if( $_ =~ /\s+artist=(.*)\n/i ){ $mp3_artist = $1 ; } 
	    if( $_ =~ /\s+album=(.*)\n/i ) { $mp3_album = $1 ;  } 
	    if( $_ =~ /\s+(date|year)=(.*)\n/i ){ $mp3_year = $2 ; }
	    if( $_ =~ /\s+tracknumber=\s*(\d+)\/?(\d+)?\n/i ){ $mp3_tn = $1 ;   }
	    if( $_ =~ /\s+tracknumber=\s*(\d+)\/(\d+)\n/i ){ $total_track = $2 }	    
	    if( $_ =~ /\s+comment=(.*)\n/i ){ $mp3_comment = $1 ;  }
	    $mp3_mpeg = "OGG Vorbis"; 
	    $bitrate_average = $true; 
   }
   $/="\n"; ## final of loop
   close(OGGINFO);
}

# only obtain info from flac files
sub get_flac_info_from_file {

   if ($metaflac_path eq "") {
       insert_msg("None informations about \"flac\" format because cant find \"" , "black");
       insert_msg("metaflac" , "red");
       insert_msg("\" in executable path.\n\n" , "black");
       return $false;
   }
   
   my $cmd; # command

   $cmd = "$metaflac_path --show-vc-field=Title  \"$directory/$file_flac\" ";
   $mp3_title = exec_cmd_system($cmd);
   if ( not defined($mp3_title) ) { $mp3_title = ""; }
   if ( $mp3_title =~ /^Title=(.*)\n/i ) {  $mp3_title = decode("utf8",$1) ; }
   
   $cmd = "$metaflac_path --show-vc-field=Artist  \"$directory/$file_flac\" ";
   $mp3_artist = exec_cmd_system($cmd);
   if ( not defined($mp3_artist) ) { $mp3_artist = ""; }
   if ( $mp3_artist =~ /^Artist=(.*)\n/i ) {  $mp3_artist = decode("utf8",$1) ; }
   
   $cmd = "$metaflac_path --show-vc-field=Album  \"$directory/$file_flac\" ";
   $mp3_album = exec_cmd_system($cmd);
   if ( not defined($mp3_album) ) { $mp3_album = ""; }
   if ( $mp3_album =~ /^Album=(.*)\n/i ) {  $mp3_album = decode("utf8",$1) ; }
   
   $cmd = "$metaflac_path --show-vc-field=Comment  \"$directory/$file_flac\" ";
   $mp3_comment = exec_cmd_system($cmd);
   if ( not defined($mp3_comment) ) { $mp3_comment = ""; }
   if ( $mp3_comment =~ /^Comment=(.*)\n/i ) {  $mp3_comment = decode("utf8",$1) ; }
   
   $cmd = "$metaflac_path --show-vc-field=Date  \"$directory/$file_flac\" ";
   $mp3_year = exec_cmd_system($cmd);
   if ( not defined($mp3_year) ) { $mp3_year = ""; }
   if ( $mp3_year =~ /^Date=(.*)\n/i ) {  $mp3_year = decode("utf8",$1) ; }
   
   $cmd = "$metaflac_path --show-vc-field=Tracknumber  \"$directory/$file_flac\" ";
   $cmd = exec_cmd_system($cmd);
   if ( not defined($cmd) ) { $cmd = ""; }   
   if( $cmd =~ /^Tracknumber=(\d+)\/?(\d+)?\n/i ){ $mp3_tn = $1 ;   }
   if( $cmd =~ /^Tracknumber=(\d+)\/(\d+)\n/i ){ $total_track = $2  }
   
   $cmd = "$metaflac_path --show-vc-field=Genre  \"$directory/$file_flac\" ";
   $mp3_genre = exec_cmd_system($cmd);
   if ( not defined($mp3_genre) ) { $mp3_genre = ""; }
   if ( $mp3_genre =~ /^Genre=(.*)\n/i ) {  $mp3_genre = decode("utf8",$1) ; }	
   
   $cmd = "$metaflac_path --show-sample-rate  \"$directory/$file_flac\" ";
   $mp3_freq = exec_cmd_system($cmd);
   if ( not defined($mp3_freq) ) { $mp3_freq = ""; } 
   else {chomp($mp3_freq); $mp3_freq = sprintf("%0.0f", $mp3_freq/1000 );  }  
   
   $cmd = "$metaflac_path --show-channels  \"$directory/$file_flac\" ";
   $channel = exec_cmd_system($cmd);
   if ( not defined($channel) ) { $channel = ""; } 
   else {chomp($channel); $channel = "Channels: $channel";} 
   
   $mp3_mpeg = "FLAC";  
   $mp3_time = "";
   
   # ( --show-total-samples * --show-bps ) / (1000 * total_time_sec)  
}


# get_info_from_entries from entry "info " of gnormalize
# for wave files
sub get_info_from_entries {
    # get mp3/ogg info to be saved on normalized file
    $mp3_title = $entry_title->get_text;
    $mp3_artist = $entry_artist->get_text;
    $mp3_album = $entry_album->get_text;
    $mp3_year = $entry_year->get_text;  
    
    $mp3_tn = ""; $total_track = "";
    if ( $entry_tn->get_text =~ /(\d+)/ ) { $mp3_tn = $1; } 
    if ( $entry_tt->get_text =~ /(\d+)/ ) { $total_track = $1; }
            
    #print " t = $mp3_tn ;; total_track = $total_track \n";
    $mp3_comment = $entry_comment->get_text;  
    $mp3_genre = $combo_genre->entry->get_text; 
}

# Set values from entries to keys: $mp3_title,$mp3_artist,...
sub array_of_tag_entry {
        # tag array
	my @array_info_from_entry = change_tag();

        # always in this order - there are 08 entries at all.
        # @array_info_from_entry = ($title,$artist,$album,$comment,$year,$tn,$total_track,$genre);
			    
        for (my $i=1;$i<=@array_info_from_entry;$i++){ 
		$array_info_from_entry[$i-1] =~ s/"/\'/g; # substitue the char " for '
         	#print("array_info_from_entry($i) = $array_info_from_entry[$i-1] \n");
        }
	
	$mp3_title = $array_info_from_entry[0];    $mp3_artist  = $array_info_from_entry[1];
	$mp3_album = $array_info_from_entry[2];    $mp3_comment = $array_info_from_entry[3];
	$mp3_year  = $array_info_from_entry[4];    $mp3_tn      = $array_info_from_entry[5];
	$total_track = $array_info_from_entry[6];  $mp3_genre   = $array_info_from_entry[7];	
	
	#-----------------------------------------------#
}

sub save_ID3v2_mp3_tag { 
    my ( $file, $title, $artist, $album, $year, $comment, $tracknum, $genre) = @_;        
    my $tmpfile; my $copy;
    my $buf_size ||= 4096*1024;  # the bigger the faster  
    
    # Masks for TAGS FLAGS
    use constant ID3_THFLAG_USYNC => 0x40; # Unsynchronisation       -  hexdecimal 0x40 = 64 decimal = (01000000) binary (bite 7)
    use constant ID3_THFLAG_EXT   => 0x20; # Extended header         -  hexdecimal 0x20 = 32 decimal = (00100000) binary (bite 6)
    use constant ID3_THFLAG_EXP	  => 0x10; # Experimental indicator  -  hexdecimal 0x10 = 16 decimal = (00010000) binary (bite 5)
    use constant ID3_THFLAG_FOOT  => 0x8;  # Footer present          -  hexdecimal 0x8  = 8  decimal = (00001000) binary (bite 4)
    
    my $track = $mp3_tn;
    if ( $total_track ne "" ){ $track = $mp3_tn."/".$total_track; }
    my $sz_yea = length($mp3_year); # size in byte
    my $sz_tra = length($track);
    my $genre_number = get_genre_number($mp3_genre);	
    #$mp3_genre = $mp3_genre;
    my $sz_gen = length("($genre_number)");
    my $sz_art = length(encode_char($mp3_artist));
    my $sz_alb = length(encode_char($mp3_album));
    my $sz_tit = length(encode_char($mp3_title));
    my $sz_com = length(encode_char($mp3_comment));
	    
    my $tagSize = 0;	    
    do { 
         use bytes; 
         $tagSize += 10 + 1 + $sz_yea;    # 10 (= frame_size) + 1 null   
	 $tagSize += $sz_tra + 11;
	 $tagSize += $sz_gen + 11;
	 $tagSize += $sz_art + 11;
	 $tagSize += $sz_alb + 11;
	 $tagSize += $sz_tit + 11;
	 $tagSize += $sz_com + 11 + 4;   # 4 for XXX<nul>
    };	 	
    
    open(IN, encode_char("$file") ) || die "Can't read $file: $!, stopped";
    
    binmode IN;        
    seek(IN, 0 , 0);  
    
    my $n = $$;
    while (1) { # make one temp file
       $tmpfile = "gnormalize"."-".$n.".tag";
       if ( open(TEMP, encode_char(">$tmpfile") ) ) { last; }
       $n++;
    }      
    
#    Overall tag ID3v2.4.0 - version structure:

#     +-----------------------------+
#     |      Header (10 bytes)      |
#     +-----------------------------+
#     |       Extended Header       |
#     | (variable length, OPTIONAL) |
#     +-----------------------------+
#     |   Frames (variable length)  |
#     +-----------------------------+
#     |           Padding           |
#     | (variable length, OPTIONAL) |
#     +-----------------------------+
#     | Footer (10 bytes, OPTIONAL) |
#     +-----------------------------+

#  The ID3v2 tag size is the sum of the byte length of the extended header, the padding and the frames after unsynchronisation. 
#  If a footer is present this equals to ('total size' - 20) bytes, otherwise ('total size' - 10) bytes.
#  The ID3v2 tag size is stored as a 32 bit synchsafe integer, making a total of 28 effective bits (representing up to 256MB).
#  Synchsafe integers are integers that keep its highest bit (bit 7) zeroed, making seven bits out of eight available. 
#  Thus a 32 bit synchsafe integer can store 28 bits of information. 
#  Example: 255 (%11111111) encoded as a 16 bit synchsafe integer is 383 (%00000001 01111111).
    
    my $sz_yea_pack = ID3_SET_SIZE28( $sz_yea + 1); # + 1 null   
    my $sz_tra_pack = ID3_SET_SIZE28( $sz_tra + 1);   
    my $sz_gen_pack = ID3_SET_SIZE28( $sz_gen + 1);
    my $sz_art_pack = ID3_SET_SIZE28( $sz_art + 1);
    my $sz_alb_pack = ID3_SET_SIZE28( $sz_alb + 1);
    my $sz_tit_pack = ID3_SET_SIZE28( $sz_tit + 1);
    my $sz_com_pack = ID3_SET_SIZE28( $sz_com + 1 + 4);  # 4 for XXX<nul>
  
    # see <man perlfunc> 
    # b: A bit string (ascending bit order inside each byte)
    my $tagVersion = pack('C C', 3, 0 );  # version ID3v2.3.0
    #my $tagFlags   = pack('b8', ID3_THFLAG_FOOT ); #has a footer - optional
    my $tagFlags   = pack('b8', 0x00 ); 
    my $tagFlagsFrame   = pack('b8 b8', 0, 0 );  
    my $min_size   = $tagSize < 1024 ? 1024 : 2230;
    $min_size      = $tagSize <  512 ?  512 : $min_size;
    my $rest       = $min_size -  $tagSize; 
    $tagSize       = ID3_SET_SIZE28($min_size); # $tagSize = 52, 1024 or 2230
        
    print TEMP pack "a3a2a1a4", 'ID3', $tagVersion, $tagFlags, $tagSize;  #Header
    print TEMP pack "a4a4a2x1a${sz_tit}", 'TIT2', $sz_tit_pack, $tagFlagsFrame, ,encode_char($mp3_title); 
    print TEMP pack "a4a4a2x1a${sz_art}", 'TPE1', $sz_art_pack, $tagFlagsFrame, ,encode_char($mp3_artist); 
    print TEMP pack "a4a4a2x1a${sz_alb}", 'TALB', $sz_alb_pack, $tagFlagsFrame, ,encode_char($mp3_album);  
    print TEMP pack "a4a4a2x1a${sz_gen}", 'TCON', $sz_gen_pack, $tagFlagsFrame, ,"($genre_number)"; 
    print TEMP pack "a4a4a2x1a${sz_yea}", 'TYER', $sz_yea_pack, $tagFlagsFrame, ,$mp3_year; 
    print TEMP pack "a4a4a2x1a3x1a${sz_com}", 'COMM', $sz_com_pack, $tagFlagsFrame, ,'XXX', ,encode_char($mp3_comment); 
    print TEMP pack "a4a4a2x1a${sz_tra}", 'TRCK', $sz_tra_pack, $tagFlagsFrame, ,$track;
    print TEMP pack "x${rest}";
    #print TEMP pack "a3a2a1a4", '3DI', $tagVersion, $tagFlags, $tagSize;  #Footer is optional
    
    # ID3    6TIT2      Prelude (Suite n.1)TPE1 ... 
    
    # sysread FILEHANDLE,SCALAR,LENGTH,OFFSET  -- Returns the number of bytes actually read, 0 at end of file,
    while ( my $ret = sysread(IN, $copy, $buf_size) ) {  
       print TEMP $copy ;  # make copy from IN file and paste to TEMP file
    }
    close(IN);
    close(TEMP);
    unlink $file;
    rename($tmpfile, $file) || die "Can't rename temp file, leaving in $tmpfile, stopped";  
    
    #my $size = ID3_GET_SIZE28($tagSize);
    #print "size = $size \n";
                 
}

# For 32 bit synchsafe integer - The Synchsafe integers are integers that keep 
# its highest bit (bit 7) zeroed, making seven bits out of eight available.
sub ID3_SET_SIZE28 {  
    my $size = shift;
    my ($a, $b, $c, $d);
    #  Example: 255 (%11111111) encoded as a 16 bit synchsafe integer is 383 (%00000001 01111111).
    # The bit-shift operators (<< and >>) : 16 >> 1 = 8 , that is (binary):  16 = (10000)   goes to   8 = (01000)
    # 255 >> 1 is equal to 127, because : 255 = (11111111)    goes to    127 = (01111111)
						
    $d = $size & 0x7f;
    $c = ( $size >> (  8-1 ) ) & 0x7f;  # 0x7f = 127 = 01111111
    $b = ( $size >> ( 16-1 ) ) & 0x7f;
    $a = ( $size >> ( 24-1 ) ) & 0x7f;
				
    #$a = sprintf( "%08b", $a ); # convert to binary base with 8 digits 
    #$b = sprintf( "%08b", $b ); # convert to binary base with 8 digits 
    #$c = sprintf( "%08b", $c ); # convert to binary base with 8 digits 
    #$d = sprintf( "%08b", $d ); # convert to binary base with 8 digits 
    #my $temp = "$a"."$b"."$c"."$d"; # binary with 32 digits 
    #print "binary = $temp \n";
    #print "decimal = ",oct( "0b$temp" ),"\n";
    
    $size = pack "C C C C", $a, $b, $c, $d;    
    return $size;
}

sub ID3_GET_SIZE28 {
    my ($x1, $x2, $x3, $x4) = unpack("C C C C", shift );
    #print "x1 = $x1 ;; x2 = $x2 ;; x3 = $x3 ;; x4 = $x4 \n";
    my $size = $x1;
    $size <<= 7;
    $size += $x2;
    $size <<= 7;
    $size += $x3;
    $size <<= 7;
    $size += $x4; 
    return $size;
    
    # the same result with:
    #my @array = reverse unpack 'C4', shift;
    #foreach my $i (0 .. 3) { $tag_size += $array[$i] * 128 ** $i; }  
}

sub remove_ID3v2_mp3_tag{ #not used
    my $fh; my $tag_size; my $file;
    
    my $buf ||= 4096*1024;  # the bigger the faster
    seek $fh, 0, 2;
    my $eof = tell $fh;
    my $off = $tag_size;

    while ($off < $eof) {
       seek $fh, $off, 0;
       read $fh, my($bytes), $buf;
       seek $fh, $off - $tag_size, 0;
       print $fh $bytes;
       $off += $buf;
    }
    truncate $fh, $eof - $tag_size or die "Can't truncate '$file': $!";
}


sub get_genre_number{
    my $genre = shift;
    my $genre_number = 12; # other : default

    if ( $genre =~ /^\d+$/ ){  # if $mp3_genre is a number
        $genre_number = $genre; 
    }       
    else{ # if $genre is a reference name, get its number
        for my $key (keys %genre_hash) {
           # print "$key => $genre_hash{$key}\n";
	   if ( $genre_hash{$key} eq $genre ){
	      $genre_number = $key;
	      last;
	   }
        } 
    }
    return $genre_number;
}

sub save_mp3_tag{
   my $file = shift;
   $file = encode_char("$file");
       
   my $genre_number = get_genre_number($mp3_genre);	
   #print "genre_number = $genre_number ;; mp3_genre = $mp3_genre\n";
       
   MP3::Info::remove_mp3tag ($file,'ALL');
       
   save_ID3v2_mp3_tag($file);
              
   save_ID3v1_mp3_tag($file, encode_char("$mp3_title"), encode_char("$mp3_artist"), 
                             encode_char("$mp3_album"), encode_char("$mp3_year"), 
			     encode_char("$mp3_comment"), $mp3_tn, $genre_number );
}


# save_tag button of table31
sub save_tag {
    my $cmd;
    
    # get all current 8 entries:
    # ($mp3_title,$mp3_artist,$mp3_album,$mp3_comment,$mp3_year,$mp3_tn,$total_track,$mp3_genre); 	  
    array_of_tag_entry();

    #verify if the choosed file is valid
    if ( $file_extension eq 'mp3' ){ 
        my $file = $directory."/".$file_mp3;
       	save_mp3_tag($file);	             
    }      
    if ( $file_extension eq 'ogg' ){
       if ($vorbiscomment_path eq "") {
          $status_bar->push($context_id, " vorbiscomment cant be found!");
          return $false;
       }              
       $cmd  = "$vorbiscomment_path -w \"$directory/$file_ogg\" -t \"comment=$mp3_comment\"  ";
       $cmd .= " -t \"title=$mp3_title\" -t \"artist=$mp3_artist\"  -t \"genre=$mp3_genre\" ";
       $cmd .= " -t \"date=$mp3_year\" -t \"album=$mp3_album\" ";
       if ( $total_track ne "" and $total_track >= $mp3_tn ) {
              $cmd .= " -t \"tracknumber=$mp3_tn/$total_track\"  ";}
       else { $cmd .= " -t \"tracknumber=$mp3_tn\"  ";}   
       exec_cmd_system($cmd);
    }    
    if ( $file_extension eq 'flac' ){
       if ($metaflac_path eq "") {
          $status_bar->push($context_id, " vorbiscomment cant be found!");
          return $false;
       }        	         
       exec_cmd_system("$metaflac_path --remove-vc-all \"$directory/$file_flac\" ");
       
       $mp3_title = encode("utf8",$mp3_title);
       $mp3_artist = encode("utf8",$mp3_artist);
       $mp3_album = encode("utf8",$mp3_album);
       $mp3_genre = encode("utf8",$mp3_genre);
       $mp3_comment = encode("utf8",$mp3_comment);
       
       # tag commands -- to see the entries: metaflac --list music.flac
       # --no-utf8-convert  decode("utf8",$1)
       my $cmd = "$metaflac_path  --set-vc-field=Title=\"$mp3_title\" --set-vc-field=Artist=\"$mp3_artist\"";
       $cmd .= " --set-vc-field=Album=\"$mp3_album\" --set-vc-field=Date=\"$mp3_year\" --set-vc-field=Comment=\"$mp3_comment\"";
       # put the total_track in file_mp3
       if ($total_track ne "" and $total_track >= $mp3_tn){$cmd .= " --set-vc-field=Tracknumber=\"$mp3_tn/$total_track\"";}
       else {$cmd .= " --set-vc-field=Tracknumber=\"$mp3_tn\"";}
       $cmd .= " --set-vc-field=Genre=\"$mp3_genre\"  \"$directory/$file_flac\"";	
	
       exec_cmd_system($cmd);	
    }
    if ( $file_extension eq 'mpc' ){	    
            remove_apetag ("$directory/$file_mpc");   	    
	    write_apetag ("$directory/$file_mpc");
    }   
    if ( $file_extension eq 'ape' ){	    
            remove_apetag ("$directory/$file_ape");   	    
	    write_apetag ("$directory/$file_ape");	    
    } 
    $status_bar->push($context_id, " $file_extension tag saved.");
    $button_save_tag->set_sensitive($false);
}

sub remove_apetag {
	    my $fname = shift;
	    my $tag_size;
	    my $APEfooter; my $preamble;

	    if (not -s encode_char("$fname")) { $@ = "File is empty"; return undef; }
	    
	    open(APE, encode_char("+< $fname\0") ) || die "Can't read $fname: $!, stopped";
	    
	    my $fh = \*APE; # fh: FileHandle passed
	    
	    binmode $fh;
	    
	    seek($fh,-32, 2);  # Set FileHandle 32 byte from the end of file
	    read($fh, $APEfooter, 32, 0); # read 32 byte to $APEfooter
	    
	    $preamble = substr($APEfooter,0,8);
            $preamble = unpack "a8",$preamble;
	    if ( $preamble ne 'APETAGEX' ){ return; }
            #print "preamble = $preamble\n";
            $APEfooter = substr($APEfooter,8 + 4);  # + 4 byte (version)	    
    
            $tag_size = substr($APEfooter,0,4);
            $tag_size = unpack "L",$tag_size;
            #print "tag_size = $tag_size\n";
	    
	    $tag_size = $tag_size + 32; # 32 byte of headear
	    	    
	    seek ($fh, -$tag_size, 2);
	    my $tell = tell $fh;
	    
	    #read($fh, $APEfooter, $tag_size, 0);
	    #print "tell = $tell  ;; APEtag = $APEfooter\n";
	    
            truncate $fh, $tell or warn "Can't truncate '$fname': $!";
	    
	    # see <man perlfunc>	     	     
	    # tell    Returns the current position in bytes for FILEHANDLE,
	    # truncate FH,LENGTH : Truncates the file opened on FILEHANDLE to the specified length.
	    # sysread FILEHANDLE,SCALAR,LENGTH,OFFSET  ;; Returns the number of bytes actually read
	    # syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET	    
	    	    
	    close($fh);			
}


sub write_apetag {
	    my $fname = shift;
	    my $tail; my $tag;  my $tag_size;
	    
	    # Masks for TAGS FLAGS
            use constant Contains_Header => 0x80000000; # hexdecimal 0x80 = 128 decimal = (10000000) binary (bite 8)
            use constant Contains_Footer => 0x40000000; # hexdecimal 0x40 =  64 decimal = (01000000) binary (bite 7)
            use constant Is_Header       => 0x20000000; # hexdecimal 0x20 =  32 decimal = (00100000) binary (bite 6)

            # convert to utf8
            $mp3_album = encode("utf8",$mp3_album) ;
            $mp3_artist = encode("utf8",$mp3_artist) ;
            $mp3_title = encode("utf8",$mp3_title) ;
            $mp3_comment = encode("utf8",$mp3_comment) ;
            $mp3_genre = encode("utf8",$mp3_genre) ;
            if ( $total_track ne "" ){ $mp3_tn = $mp3_tn."/".$total_track; }
	    
	    if (not -s encode_char("$fname") ) { print "File <$fname> is empty: $@"; return undef; }
	    
	    open(APETAG, encode_char(">>$fname") ) || die "Can't read $fname: $!, stopped";	    
	    binmode APETAG; 

	    seek APETAG, 0, 2; # go to end of file : 2 to set it to EOF (End Of File)
	    
	    my $sz_yea = length($mp3_year); # size in byte
	    my $sz_tra = length($mp3_tn);
	    my $sz_gen = length($mp3_genre);
	    my $sz_art = length($mp3_artist);
	    my $sz_alb = length($mp3_album);
	    my $sz_tit = length($mp3_title);
	    my $sz_com = length($mp3_comment);
	    
	    my $tagTotalSize = 0;	    
	    do { 
	       use bytes; 
	       $tagTotalSize += 8 + length('Year') + 1 + $sz_yea;
	       $tagTotalSize += length('Track') + $sz_tra + 9;
	       $tagTotalSize += length('Genre') + $sz_gen + 9;
	       $tagTotalSize += length('Artist') + $sz_art + 9;
	       $tagTotalSize += length('Album') + $sz_alb + 9;
	       $tagTotalSize += length('Title') + $sz_tit + 9;
	       $tagTotalSize += length('Comment') + $sz_com + 9; 
	    };	 	    
	    # print TAG to file
	    # pack TEMPLATE,LIST  --- see <man perlfunc>
	    my $copy_size = $tagTotalSize;
	    
            # L: An unsigned long value. 
	    my $tagVersion      = pack('L', 2000 ); # 2000 =   
	    $tagTotalSize       = pack('L', $tagTotalSize + 32 );  # + 32 byte (Footer)
	    my $tagTotalItems   = pack('L', 7 ); # 7 itens : year,tn,artist,...,comment
	    my $tagGlobalFlags  = pack('L', Is_Header );
	    my $tagGlobalFlags2 = pack('L', Contains_Header );
 
	    # initial - header
	    # APETAGEX                          Year 1975
	    	    
	    print APETAG pack "a8a4a4a4a4x8", 'APETAGEX', $tagVersion, $tagTotalSize, $tagTotalItems, $tagGlobalFlags;
	    print APETAG pack "a8a5a${sz_yea}", pack('L', $sz_yea ), 'Year', $mp3_year;
	    print APETAG pack "a8a6a${sz_tra}", pack('L', $sz_tra ), 'Track', $mp3_tn;
	    print APETAG pack "a8a6a${sz_gen}", pack('L', $sz_gen ), 'Genre', $mp3_genre;
	    print APETAG pack "a8a7a${sz_art}", pack('L', $sz_art ), 'Artist', $mp3_artist;
	    print APETAG pack "a8a6a${sz_alb}", pack('L', $sz_alb ), 'Album', $mp3_album;
	    print APETAG pack "a8a6a${sz_tit}", pack('L', $sz_tit ), 'Title', $mp3_title;
	    print APETAG pack "a8a8a${sz_com}", pack('L', $sz_com ), 'Comment', $mp3_comment;
	    print APETAG pack "a8a4a4a4a4x8", 'APETAGEX', $tagVersion, $tagTotalSize, $tagTotalItems, $tagGlobalFlags2;

	    # final - footer
	    #APETAGEX                   

# from taglib-1.3.1: taglib/ape/ape-tag-format.txt  --- Member of APE Tag 2.0
# http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html
#/===========================================================================\
#| Preamble       | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' }     |
#|----------------|---------|------------------------------------------------|
#| Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000     |
#|----------------|---------|------------------------------------------------|
#| Tag Size       | 4 bytes | Tag size in bytes including footer and all tag |
#|                |         | items excluding the header (for 1.000          |
#|                |         | compatibility)                                 |
#|----------------|---------|------------------------------------------------|
#| Item Count     | 4 bytes | Number of items in the tag                     |
#|----------------|---------|------------------------------------------------|
#| Tag Flags      | 4 bytes | Global flags                                   |
#|----------------|---------|------------------------------------------------|
#| Reserved       | 8 bytes | Must be zeroed                                 |
#\===========================================================================/


#                           APE Tag Flags
#/=================================================================\
#| Contains Header | Bit 31      | 1 - has header | 0 - no header  |
#|-----------------|-------------|---------------------------------|
#| Contains Footer | Bit 30      | 1 - has footer | 0 - no footer  |
#|-----------------|-------------|---------------------------------|
#| Is Header       | Bit 29      | 1 - is header  | 0 - is footer  |
#|-----------------|-------------|---------------------------------|
#| Undefined       | Bits 28 - 3 | Undefined, must be zeroed       |
#|-----------------|-------------|---------------------------------|
#| Encoding        | Bits 2 - 1  | 00 - UTF-8                      |
#|                 |             | 01 - Binary Data *              |
#|                 |             | 10 - External Reference **      |
#|                 |             | 11 - Reserved                   |
#|-----------------|-------------|---------------------------------|
#| Read Only       | Bit 0       | 1 - read only  | 0 - read/write |
#\=================================================================/

# http://www.hydrogenaudio.org/musepack/klemm/www.personal.uni-jena.de/%7Epfk/mpp/sv8/apevalue.html
# APE Tags Header  32 byte
# APE Tag Item 1   10 byte   : APE tags items should be sorted ascending by size.
# APE Tag Item 2   10 byte
# ...
# APE Tag Item n   10 byte
# APE Tags Footer  32 byte


#                          Item:  length key value
# APE Tag Item:   4 byte : Length len of the assigned value in bytes
#	    	  4 byte : Item flags
#	    	  m byte : Item key, can contain ASCII characters from 0x20 (Space) up to 0x7E (Tilde)
#	    	  1 byte : Item key terminator   0x00
#	    	  len byte : Item value, can be binary data or UTF-8 string
		     		     
	    close(APETAG);	
	    #print "size = ",bin2dec(unpack('B32', '' )),"\n";	#219	
	    #print "genre = $mp3_genre ;; size = $sz_gen \n";
	    #$tagGlobalFlags2 = bin2dec( unpack('B32', '' ) );
	    #$tagGlobalFlags2 =  pack('L', TF_HAS_HEADER );
	    #$tagGlobalFlags2 = bin2dec( unpack('B32', $tagGlobalFlags2 ) );
	    #print "tagGlobalFlags2 = $tagGlobalFlags2 ;;\n";	    
}	

sub bin2dec {
	# see <man perlfunc>
	return unpack ('N', pack ('B32', substr(0 x 32 . shift, -32)));
}

#####-----------------------------------------------#####

##--------------------- Character ---------------------##

# substitute the command <system()> by this sub routine <exec_cmd_system()>
# this occur because of error on loading character such that , , , ...
# in the filename and dirname. This is character encoding routine on
# input/output via a filehandle <open()>.
# see man Encode. utf8, PerlIO, perlrun, ...
sub exec_cmd_system {
   my ($cmd) = @_;  # $cmd is a string
   my $value;
   # "to convert a string from Perl's internal format to iso-8859-1 (also known as Latin1)
   #  use:   <$octets = encode("iso-8859-1", $string);>  "
   #  in my case:  $character = 'iso-8859-1'
   # open filehandle, filename
   open (CM, encode("$character","$cmd 2>&1 |") ) or die "couldn't execute \"$cmd\": $!";
   #print "-->\"$cmd\": $! \n";
   $/="\r"; ## start of loop
   while(<CM>){ $value=$_; } #$_ is the output of command
   $/="\n"; ## final of loop 
   close(CM);
   return $value; 
}

# change the character
sub encode_char {
   my ($cmd) = @_;    
   # iso-8859-1 is also known as Latin1
   encode("$character","$cmd");
}

# change, for exemple, 0,49 to 0.49
sub number_value{
    my $value = shift;
    $value =~ s/,/./g;
    return $value;
}

#####-----------------------------------------------#####

sub minimum {
  my $v1 = shift;
  my $v2 = shift;
  my $min;
  if ( $v1 < $v2 ) { $min = $v1; }
  else { $min = $v2; }
  return $min;
}


# if  ( the file of input have the same extension of output and $check_button_change is not active
# and $check_button_overwrite is not active then make copy. 
sub same_extension_overwrite {
    my $cmd;
    if ( $file_extension eq $file_output and not $check_button_change->get_active){ # wave not satisfy this condition
         # always remove file_wav_copy if it exist and dont need to encode
         if ( -e encode_char("$directory/$file_wav_copy") ){ 
	      exec_cmd_system("rm -f \"$directory/$file_wav_copy\" "); 
         }
	 #remove wav file if button_del_wav is active and $file_wav exist
	 if ($check_button_del_wav->get_active and -e encode_char("$directory/$file_wav") ){ 
	      exec_cmd_system("rm -f \"$directory/$file_wav\" "); 
         }
         # if overwrite is active, dont need copy file
	 if ( not $check_button_overwrite->get_active){
	      # copy the mp3/ogg file (that dont need to normalize) to $directory_final
	      if ($file_extension eq 'mp3'){ $cmd = "cp -f \"$directory/$file_mp3\"  \"$directory_final\" ";}
              if ($file_extension eq 'ogg'){ $cmd = "cp -f \"$directory/$file_ogg\"  \"$directory_final\" ";}
	      if ($file_extension eq 'mpc'){ $cmd = "cp -f \"$directory/$file_mpc\"  \"$directory_final\" ";}
	      if ($file_extension eq 'ape'){ $cmd = "cp -f \"$directory/$file_ape\"  \"$directory_final\" ";}
	      if ($file_extension eq 'flac'){ $cmd = "cp -f \"$directory/$file_flac\"  \"$directory_final\" ";}
	      insert_msg("$cmd\n" , "small"); #print on debug textview
	      insert_msg("copy maked" , "small-red"); 
	      insert_msg("\n" , "small");
	      exec_cmd_system($cmd);
	 } 
         if ( not $already_normalized ) {
	     $pbar_n->set_fraction(0) ; 
	     my $sb = $spinner_sensi->get_value;
	     if ( abs($adjust) <= $sb ){
	         $pbar_n->set_text(" according to insensibility, not normalize ");
		 insert_msg("Not normalize because adjustment " , "small"); 
		 insert_msg("|$adjust| <= $sb" , "small-red");
		 insert_msg(" insensibility value.\n" , "small"); 
		 insert_msg("-----*****-----\n" , "small-black");
             }
	     else { $pbar_n->set_text(" not normalize ");} # for type = None
         }	
         $pbar_lame->set_fraction(0) ;  
         $pbar_lame->set_text(" dont need encode ");
         $status_bar->push($context_id, " ");
    }
}

sub finalize_process {        
        my $file = shift;
	my $cmd;

        #remove wav file if button_del_wav is active and $file_wav exist
	if ($check_button_del_wav->get_active and -e encode_char("$directory/$file_wav") ){ 
	     exec_cmd_system("rm -f \"$directory/$file_wav\" "); }
	  
	# At final of encode, always remove file_wav_copy if it exist
	if ( -e encode_char("$directory/$file_wav_copy") ){  # if the file exists
                exec_cmd_system("rm -f \"$directory/$file_wav_copy\" ");
	}
	
	# overwrite: yes or not? That is the question.
	# overwrite only if the encoding process have finished, that is, $normalize_button is active
	if ($normalize_button->get_active and $check_button_overwrite->get_active and -e encode_char("$directory_final/$file")){
	     $cmd = "mv -f \"$directory_final/$file\"  \"$directory/$file\"  ";
	     exec_cmd_system($cmd); # overwrite
	     insert_msg("\n$cmd\n" , "small");
	     exec_cmd_system("rmdir \"$directory_final\"  "); # remove the directory if empty
	}
	
	## set encoding on the final of process
	if ($normalize_button->get_active) {  # click on the normalize button is active
	     insert_msg("-----*****-----\n" , "small-green");
	     $pbar_lame->set_fraction( 1 );
	}
	elsif ( -e encode_char("$directory_final/$file") ) { # if the file exists
	     #remove normalized file not finished
	     exec_cmd_system("rm -f \"$directory_final/$file\" ");
	}	
}


# determine the values of 7 encode variables: $encode, $mode,
# $vb_Min, $vb_Max, $bitrate, $V, $frequency
# dont need to determine $quality

sub encode_settings { 
	my $encode;
	my $mode;
	my $vb_Min;
	my $vb_Max;
	my $bitrate; 
	my $frequency;
	
	if ( $mp3_freq == 44 ){ $mp3_freq = 44.1;  } # set value with more accuracy
        if ( $mp3_freq == 11 ){ $mp3_freq = 11.025;} 
        if ( $mp3_freq == 22 ){ $mp3_freq = 22.05; }
	$mp3_freq = number_value($mp3_freq); 
	
	# button "change properties" active
	if ( $check_button_change->get_active ){ 
	       $mode = $combo_mode->entry->get_text;
	       $encode = $combo_const_avg->entry->get_text();   # refresh value
	       if ( $file_output eq 'mp3' ) { $bitrate = $spinner_bitrate_mp3->get_value_as_int(); }# for $encode = "average"
	       elsif ( $file_output eq 'ogg' ) { $bitrate = $spinner_bitrate_ogg->get_value_as_int(); }# for $encode = "average"
	       $frequency = number_value($combo_fq->entry->get_text);
	       # for mp3 constant $bitrate is obtainned from $combo_bitrate
	       if ( $encode eq "constant" and $file_output eq 'mp3' ) { $bitrate = $bitrate_constant;} 
	       $V = $spinner_V->get_value_as_int; # Vmp3
	       $Vogg = $spinner_Vogg->get_value;
	       $Vmpc = $spinner_Vmpc->get_value;
	}
	# button "change properties" not active
	# decide if the bitrate of normalized files will be preserved
	else { 
	       $bitrate = $bitrate_original; # copy the value of original bitrate
	       $frequency = $mp3_freq;  
	       # only this two possibility when button "change properties" is not active
               if ( $bitrate_average ){ $encode = "average"; } 
	       else { $encode = "constant";}
	       
	       if ( $file_output eq 'mpc' ){
	          # estimating a fuction to find $V from $bitrate: $V = f($bitrate)
	          my $x = number_value($bitrate_original);
	          my $adjustm = 0.15*exp(-( ($x-160)^2 )/8000) ;
	          $Vmpc = sprintf("%0.2f", ($x/29)*(1 - $adjustm) - 0.2 ); 
		  $Vmpc = number_value($Vmpc); 
	          #print("bitrate = $x; ajust = $adjustm; V = $V \n");
	       } 
	       if ( $file_extension =~ /(wav|cda|ape|flac)/ ){ 
	            $bitrate = 160; 
		    $frequency = 44.1;
		    $Vogg = $V = 4.0;
		    $Vmpc = 5.0;
		    $encode = "average";
	       }
	       if ( $file_extension eq 'mp3' and $mode_original ne "" ){ $mode = $mode_original;}
	       else {$mode = 'joint-stereo';}
	}
        $Vogg = sprintf("%0.1f", $Vogg ); $Vogg = number_value($Vogg); # only one digit
	$Vmpc = sprintf("%0.1f", $Vmpc ); $Vmpc = number_value($Vmpc);
	
	$vb_Max = $combo_Max->entry->get_text; #refresh value
        $vb_Min = $combo_Min->entry->get_text;	  
	
	if ( $file_output eq 'ogg' ){ $frequency = $frequency*1000; }
	# for MPEG-2   layer III sample frequencies (kHz):  16  24  22.05
        # bitrates (kbps):  8 16 24 32 40 48 56 64 80 96 112 128 144 160
	if ( $frequency <= 24 and $file_output eq 'mp3') { 
	     $vb_Min = minimum($vb_Min, 160);
	     $vb_Max = minimum($vb_Max, 160);
	     $bitrate = minimum($bitrate, 160);
	}		
	return ($encode,$mode,$bitrate,$frequency,$vb_Min,$vb_Max);
}

##-----------------------CDDB--------------------------##
# See to <man CDDB_get>
# Read the Album, Artist and Track Titles

sub cddb_info {
	
        my %config;
	my $audiodevide_path = $entry_cda->get_text;
	my %cd;

        # following variables just need to be declared if different from defaults
        $config{CDDB_HOST}="freedb.freedb.org";        # set cddb host
        $config{CDDB_PORT}=8880;                       # set cddb port
        $config{CDDB_MODE}="cddb";                     # set cddb mode: cddb or http
        $config{CD_DEVICE}="$audiodevide_path";        # set cd device
	my $cddbid = get_discids($audiodevide_path)->[0];
        $cddbid = sprintf("%08x", $cddbid);
	#print "cddbid = $cddbid\n";

        # user interaction welcome?

        $config{input}=0;   # 1: ask user if more than one possibility
                            # 0: no user interaction		    
        
	my $info_cddb = ""; 
	$info_cddb = get_info_cda_from_home(); # return the cddb filename
	if ( $info_cddb ne "" ){	
           $status_bar->push($context_id, " Getting information from $info_cddb");
	   return;
	}
	else{
	   eval {%cd = get_cddb(\%config);};  # get info from internet
	   if($@) { 
	      $status_bar->push($context_id, " cddb: cant connect to internet!");
	      return;
	   }
	}
	(my $album = $cd{title} ) =~ s/[:;*?|><"\$!\015]//g;	# remove some chars
	$entry_album->set_text($album);
	(my $genre = $cd{genre} ) =~ s/[:;*?|><"\$!\015]//g;	
	$combo_genre->entry->set_text($genre);
	( my $year = $cd{year} ) =~ s/[\015]//g; 	
	#print "year = $year \n";
        $entry_year->set_text($year);
	my $artist = $cd{artist};
		
	my $model = $treeview4->get_model;
	
	# criate a directory: "$home/.cddb" if it not exist
	if ( not opendir(DIR, encode_char("$home/.cddb") ) ){
	      my $cmd = "mkdir \"$home/.cddb\" "; # create the directory .cddb
              exec_cmd_system($cmd); 
	}
	close(DIR);
	
	# save cddb get from internet at home/.cddb/$cddbid
	my $file = "$home/.cddb/$cddbid";	
	open(CDDB, "> $file ") || die "Can not write to $file: $!";
	  print CDDB "# created by gnormalize - $VERSION\n";
	  print CDDB "# homepage: $HOMEPAGE\n\n";
	  print CDDB "DISCID=$cddbid\n";
	  print CDDB "DTITLE=$artist / $album\n";
	  print CDDB "DYEAR=$year\n";
	  print CDDB "DGENRE=$genre\n";
	
	# change the entries of rip		
	my $n=1;
        foreach my $titulo ( @{$cd{track}} ) {
          #print "track $n: $tit\n";
	  my $row = $n-1;
	  my $iter = $model->get_iter_from_string ("$row");
	  
	  $titulo =~ s/[:;*?|><"\$!\015]//g;
	  $titulo =~ s/`/'/g;
	  $music_cda[$n-1]{title} = $titulo;
	  #print "tituloN = $titulo\n";
	  $model->set ($iter, COLUMN_TITLE, $music_cda[$n-1]{title});

	  print CDDB "TTITLE$row=$titulo\n";
	  
	  $music_cda[$n-1]{artist} = $cd{artist};
	  $model->set ($iter, COLUMN_ARTIST, $music_cda[$n-1]{artist});
          $n++;
        }
	close CDDB;
		      
}
##-----------------------------------------------------##  

sub get_info_cda_from_home { 
        my $audiodevide_path = $entry_cda->get_text(); # /dev/hdc or /dev/cdrom or ...
        my $model = $treeview4->get_model;
	my $info_cddb = "";

        # Get number of ID and number of tracks of CD.
	my $cddbid = get_discids($audiodevide_path)->[0];
	$cddbid = sprintf("%08x", $cddbid);
	my $tracks = get_discids($audiodevide_path)->[1];
	#print "cddbid = $cddbid ;; tracks = $tracks \n";
	
	opendir(DIR, encode_char("$home/.cddb") ) ;  #search for $genre/$cddbid file
	while (my $dir = readdir(DIR)) {
		next if $dir eq ".";
		next if $dir eq "..";
		if ( -e "$home/.cddb/$dir/$cddbid" ){
		   $info_cddb = "$home/.cddb/$dir/$cddbid";
		   last;
		}
		#print "dir = $dir ;; info_cddb = $info_cddb\n";
	}
	closedir(DIR);
        
	# this is prefered
	if ( -e "$home/.cddb/$cddbid" and $info_cddb eq "" ){$info_cddb = "$home/.cddb/$cddbid";}
	#print "info_cddb = $info_cddb\n";
	
	#input:	
	#DISCID=29107b14
        #DTITLE=Alceu Valenca / Focus
        #DYEAR=1999
        #DGENRE=unknown
        #DID3=12
        #TTITLE0=Estacao da luz
        #TTITLE1=petalas
        #TTITLE2=leque moleque
	
	if ( $info_cddb eq "" ){ return $info_cddb;}
	
	# read from file
	open(INFOCDDA, "<$info_cddb") ;
	my $artist_cda = "artist";
	my $artist_album = "album";
	my $genre = "";
	
	# TITLES of the songs
	while( <INFOCDDA> )
	{   
	     if ($_ =~ /DTITLE=(.*)\s\/\s(.*)\n/i){ 
	        $artist_cda = $1;
		$mp3_album = $2; 
	     }
	     if ($_ =~ /DYEAR=(.*)\n/i){ $mp3_year = $1; }
	     if ($_ =~ /DGENRE=(.*)\n/i){ $genre = $1; }
             if ($_ =~ /TITLE(\d+)=(.*)\n/i){ 
	        #print "TITLE$1 = $2\n";
		my $row = $1;
		my $path = Gtk2::TreePath->new($row);
                my $iter = $model->get_iter($path);
		if ( $artist_cda ne "" ){
		   $music_cda[$row]{artist} = $artist_cda;
		   $model->set ($iter, COLUMN_ARTIST, $music_cda[$row]{artist});
		}
		if ( $2 ne "" ){
		   $music_cda[$row]{title} = $2;
		   $model->set ($iter, COLUMN_TITLE, $music_cda[$row]{title});
		}
	     }
	}
	close(INFOCDDA);
	$entry_album->set_text($mp3_album);
	#print "mp3_album = $mp3_album\n";
	$combo_genre->entry->set_text($genre);
        $entry_year->set_text($mp3_year);
	#$min = get_discids($audiodevide_path)->[2]->[$i]->{min};
	#$sec = get_discids($audiodevide_path)->[2]->[$i]->{sec};
	#$total = $sec + 60 * $min;
	
	return $info_cddb;	
}

#  cda --> wav   ; rip audio cd with cdparanoia

sub refresh_cda { 

# -Q --query  Perform  CDROM  drive autosense, query and print the CDROM table
#             of contents, then quit.
        stop_playing_cda();

	if ( $cdparanoia_path eq "" ){
           $status_bar->push($context_id, " Cant rip audio CD! cdparanoia not found.");
           $file_extension = 'error';
           return $false;
        }
	if ( $ripper eq "cdda2wav" and $cdda2wav_path eq "" ){
           $status_bar->push($context_id, " Cant rip audio CD! cdda2wav not found.");
           $file_extension = 'error';
           return $false;
        }

        # clear the old model - see <man Gtk2::ListStore>
	$model->clear;
	# refresh tree view
        $treeview4->set_model($model);
	
	my $audiodevide_path = $entry_cda->get_text(); # /dev/hdc
	#my $cdparanoia_path = '/usr/bin/cdparanoia';
	
	# cdda2wav  dev=/dev/hdc  -J -v toc
	# $cdda2wav_path  dev=$audiodevide_path  -J -v toc
	
	$priority = $spinner_pri->get_value;
        
        $status_bar->push($context_id, " Please, wait. cdparanoia is reading the cdrom ...");	
	while (Gtk2->events_pending()) { Gtk2->main_iteration()};
         	
	my $cmd = "nice -+$priority $cdparanoia_path  -Q -d $audiodevide_path ";	
	
	my $list = exec_cmd_system($cmd);
		
	@music_cda = ();    # reset array
	@music_sector = (); # array of music size
	@first_sector = ();
	my @length = ();    # array of music length
	
	if ( $list =~ /Unable\sto\sopen\sdisc/is or $list =~ /Unable\sto\sopen\scdrom\sdrive/is ){
	   $button_selec->set_sensitive($false);
           $da->set_sensitive($false);
	   $status_bar->push($context_id, " Unable to open cdrom drive $audiodevide_path.");
	   return $false;
	}
	#output:	
	# Table of contents (audio tracks only):
        #track        length               begin        copy pre ch
        #===========================================================
        #  1.    11900 [02:38.50]        0 [00:00.00]    no   no  2
        #  2.    21945 [04:52.45]    11900 [02:38.50]    no   no  2
	#  3.    18505 [04:06.55]    33845 [07:31.20]    no   no  2
	# 18.    15580 [03:27.55]   259607 [57:41.32]    no   no  2
	# <man perlrequick>
	
	# @length is an array with length of the songs
	while( $list =~ /\s+\d+\.\s+(\d+)\s+\[(.*):(\d+\.\d+)\]\s+(\d+)\s+\[/g )
	{   
	   my $sec = number_value(sprintf("%02d", $3 )); # 02d: leading zero
	   push @length ,"$2:$sec" ;  
	   push @music_sector ,"$1" ; 
	   push @first_sector , $4;   
	}
	
	# clear the old model - see <man Gtk2::ListStore>
	#$model->clear;	
	# create the new tree model
        my $model = create_model (@length);
	
	# update - remove the old $treeview = $sw->child
	# $sw->remove ($sw->child);
	
	# refresh tree view
        $treeview4->set_model($model);	
	
	if ( @music_cda >= 1 ){ # exist, at least, one track
	   $normalize_button->set_label("rip + normalize + encode");
	   $force_unselect_all_tracks = $false; # to select all tracks
	   unselect_cda();  # subroutine that select all tracks
	   $button_unselec->set_sensitive($true);
	   $button_selec->set_sensitive($true);
	   if ($cdplay_path ne "" or $cdcd_path ne "" or $use_audiocd eq $true){
	      $da->set_sensitive($true);
	      @tracks_already_played = ();
              @tracks_already_played_time = ();
	      $go_forward = $false;
              $button_go_forward->set_sensitive($true);
	      $go_back = $false;
              $button_go_back->set_sensitive($true);
	   }
	   pbar_erase_name();  # erase progress bar names
	   $file_extension = 'cda';
	   get_info_from_file(); # erase all old informations from info of gnormalize
	   draw_input_output($da_arrow_dec,undef,("cda","wav")); 
	   my $file = " ";
	   ($file_mp3,$file_ogg,$file_mpc,$file_ape,$file_flac,$file_wav,$file_wav_copy) = file_with_extension($file);
           $tooltips->set_tip( $entry_f, "Select a mp3/mpc/ogg/ape/flac/wav file."); # entry as beginning
	   
	   if ($check_button_cddb->get_active) { 
	      $status_bar->push($context_id, " Getting information from cddb ...");
	      while (Gtk2->events_pending()) { Gtk2->main_iteration()};
	      cddb_info();
	   }
	   else  { $status_bar->push($context_id, " none cddb information.");}
	}	
	return @music_cda;	
}

sub add_columns {
  my $treeview = shift;
  my $model = $treeview4->get_model;

  # column for fixed toggles
  my $renderer = Gtk2::CellRendererToggle->new;
  $renderer->signal_connect (toggled => \&fixed_toggled);

  my $column = Gtk2::TreeViewColumn->new_with_attributes ("rip", $renderer, active => COLUMN_RIP);
  # set this column to a fixed sizing (of 40 pixels)
  $column->set_sizing ('fixed');
  $column->set_fixed_width (40);
  $column->set (alignment => 0.5);
  $treeview->append_column ($column);

  # column for tracks
  $renderer = Gtk2::CellRendererText->new;
  $renderer->set ('xalign' => 0.5);
  $column = Gtk2::TreeViewColumn->new_with_attributes ("track", $renderer, text => COLUMN_TRACK);
  $column->set_sort_column_id (COLUMN_TRACK);
  $column->set_min_width (50);
  $column->set_resizable ($true);
  $column->set (alignment => 0.5); # Alignment of the column header text
  #$column->set_expand ($true);
  $treeview->append_column ($column);
  
  # artist column
  $renderer = Gtk2::CellRendererText->new;
  $renderer->signal_connect (edited => \&cell_edited);
  $renderer->set_data (column => COLUMN_ARTIST);
  $renderer->set (font => 'italic', scale => '0.9');
  $column = Gtk2::TreeViewColumn->new_with_attributes ("artist", $renderer, text => COLUMN_ARTIST, 
                                                        editable => COLUMN_EDITABLE);
  $column->set_resizable ($true);
  $column->set_min_width (120);
  $column->set_sort_column_id (COLUMN_ARTIST);
  $column->set (alignment => 0.0); # Alignment of the column header text
  $treeview->append_column ($column);		

  # title column
  $renderer = Gtk2::CellRendererText->new;
  $renderer->signal_connect (edited => \&cell_edited);
  $renderer->set_data (column => COLUMN_TITLE);
  $renderer->set (font => 'italic', scale => '0.9');
  $column = Gtk2::TreeViewColumn->new_with_attributes ("title", $renderer, text => COLUMN_TITLE, 
                                                        editable => COLUMN_EDITABLE);
  $column->set_resizable ($true);
  #$column->set_min_width (220);
  $column->set_sort_column_id (COLUMN_TITLE);
  $column->set (alignment => 0.0);
  $column->set_expand ($true);  # this column expand to max width
  $treeview->append_column ($column);
    
  # length column
  $renderer = Gtk2::CellRendererText->new;
  $column = Gtk2::TreeViewColumn->new_with_attributes ("length", $renderer, text => COLUMN_LENGTH);
  $column->set_sort_column_id (COLUMN_LENGTH);
  $renderer->set ('xalign' => 0.0);
  $column->set_min_width (50);
  $column->set_resizable ($true);
  $column->set (alignment => 0.0);
  $treeview->append_column ($column);
  
}

sub fixed_toggled {
  my ($cell, $path_string) = @_;
  my $path = Gtk2::TreePath->new ($path_string); # $path_string is the row number
  
  #my $path = $treeview4->get_path_at_pos ($path_string);
  
  # this is necessary when the column Track or Length is reordenable or altered.
  my $model = $treeview4->get_model;
  my $iter = $model->get_iter($path);
  #print ("model = ",$model->get ($iter)," \n");
  $track = $model->get_value($iter,COLUMN_TRACK);
  #print ("track = $track \n");
  
  #my $row = ($path->get_indices)[0];
  my $row = $track-1;
   
  # change the boolean value
  $music_cda[$row]{rip} ^= 1;
  # set new value
  $model->set ($iter, COLUMN_RIP, $music_cda[$row]{rip});
  #print ("track = $track ;; rip[$row] = ",$music_cda[$row]{rip}," \n");
} 

sub cell_edited {
  my ($cell, $path_string, $new_text) = @_;
  # $path_string is the row number
  #print (" path_string = $path_string \n");
  my $path = Gtk2::TreePath->new_from_string ($path_string);

  my $column = $cell->get_data ("column"); #column number = 2 for title
  my $model = $treeview4->get_model;
  my $iter = $model->get_iter ($path);
  
  my $track = $model->get_value($iter,COLUMN_TRACK); # track number
  #my $track2 = $music_cda[$row]{track}; # Note that this dont work
  #print ("track = $track ;; track2 = $track2  ;; row = $row\n");
  my $row = $track-1;
  #my $row = ($path->get_indices)[0];

  # $iter (Gtk2::TreeIter) is a (list) of column indices
  # for example, $iter = 110music title04:011
  # such that: 'rip'=1 ; 'track'=10 ; 'title'=music title ; 'length'=04:01 ; 'editable'=1
  #print(" model = ", $model->get ($iter) ," \n");
  #my $iter2 = "$music_cda[0]{rip}$music_cda[0]{track}$music_cda[0]{title}$music_cda[0]{length}$music_cda[0]{editable}" ;
  #print(" iter2 = $iter2 \n");
  
  if ($column == COLUMN_ARTIST) {
	$music_cda[$row]{artist} = $new_text;
	$model->set ($iter, $column, $music_cda[$row]{artist});
  }
  if ($column == COLUMN_TITLE) {
	$music_cda[$row]{title} = $new_text;
	$model->set ($iter, $column, $music_cda[$row]{title});
	#print ("--> title = ",$music_cda[$row]{title},";;  rip = ",$music_cda[$row]{rip}," track = $track\n");
  }
}

# see examles/customlist ; Gtk2::TreeModel
sub unselect_cda { # unselect all tracks
  my $model = $treeview4->get_model;
  
  if ( $#music_cda == 0 ){ return $false ;}

  for (my $row = 0; $row <= $#music_cda ; $row++){   
       if ( $force_unselect_all_tracks eq $true ){ $music_cda[$row]{rip} = $false; }
       else { $music_cda[$row]{rip} = $true; }
       my $iter = $model->get_iter_from_string ("$row");
       $model->set ($iter, COLUMN_RIP, $music_cda[$row]{rip});
  }
  $align_selec->remove ($align_selec->child);
  
  if ( $force_unselect_all_tracks eq $false ){ $align_selec->add ($button_unselec); }
  else { $align_selec->add ($button_selec); }
  
  $force_unselect_all_tracks ^= 1; # change boolean variable
}

# get treeview4 selection and return the track selected and its total time in seconds
sub selection_tree { #not used
  my $audiodevide_path = $entry_cda->get_text(); # /dev/hdc or /dev/cdrom or ...
  my $tree_selection = $treeview4->get_selection;
  my ($model, $iter) = $tree_selection->get_selected;
  if ( not defined($iter) ){
     $status_bar->push($context_id, " Please, select one track to play.");  
     return -1;
  }
  my $column = COLUMN_TRACK;
  my $selected = $model->get_value($iter,$column); # track number
  my $row = $selected - 1;  # $track-1;
  my $time = $music_cda[$row]{length}; # 2:56:34   02:32
  $time = time_to_sec($time);
  return ($selected, $time) ;
}

# this sub came from code in C (display.c) of galculator with modifications to gtk2-perl
sub button_press_event2 {  
  my $widget = shift;	  # Gtk2::TextView  
  my $event = shift;	  # GdkEventButton *event  : see Gtk2::Gdk::Event
  my ($x, $y);            # $x and $y are integers coordenate 
  my ($start, $end);      # $start and $and are (Gtk2::TextIter)
  # my $hsize_of_box = 130; # size of Gtk2::EventBox
  my $selected_text;

  if ($event->button == 1) {
      ($x, $y) = $widget->get_pointer;
      #$start = $textview_play->get_iter_at_location ($x, $y);
      #$end =  $textview_play->get_iter_at_location ($x, $y);  # $end = $start;  dont work!!
      if ( $start->is_end  eq $true)   { return $false;} # return if clicked outside of textview.
      if ( $start->is_start  eq $true) { return $false;}
      if (not $start->starts_word) { $start->backward_word_start;}
      if (not $end->ends_word) { $end->forward_word_end;}
      
      # if ( $x/$hsize_of_box <= 0.38 )   { $selected_text = "left_side";  } # split the display
      # elsif ( $x/$hsize_of_box >= 0.44 ){ $selected_text = "right_side"; }
      #my $buffer = $widget->get_buffer;
      #my $selected_text = $buffer->get_text($buffer->get_start_iter, $buffer->get_end_iter, $false);
      #my $display_text = $buffer->get_text($start, $end, $true);
      
      #print "-->display_text =$display_text \n";
      
      # in a some case, we get two options as selected_text
      # if ( $display_text =~ /(play|stop)\s+rnd/ ){ return $false;} # word with spaces
      #if ( $display_text =~ /rnd/ ){ $selected_text = "rnd"  ; } #randon
      #if ( $display_text =~ /(play|stop)/ ){ $selected_text = $display_text; }
      #if ( $display_text =~ /[\d+:-]/ ){ $selected_text = "right_side"; }   # if have 00:00 or -12: or - ...
  }
  else { return; }  
  # print "  selected_text =$selected_text ;; total_time = $total_time\n";
  $go_back = $false;
  $go_forward = $false;
  
  if ( $selected_text eq "rnd" )   { 
     $play_random ^= 1; 
     show_time();
     return;
  }  
  if ( $selected_text eq "right_side"  and $show_time_remaining eq $false ){ 
     if ( $total_time >= 1){ $show_time_remaining = $true; }
     show_time();
     return;
  }
  elsif ( $selected_text eq "right_side" and $show_time_remaining eq $true ){ 
     $show_time_remaining = $false; 
     show_time();
     return;
  }
  if ( $selected_text eq "stop" ){ 
     stop_playing_cda(); 
     return;
  }
  play_selection();    
  return $true;
}

sub play_selection {
  my $selected_track_before = shift;
  my $model = $treeview4->get_model; 
  
  # when go_back, the selected_track is already known from @tracks_already_played
  if ( $go_back ne $true ){ 
     ($selected_track, $total_time) = track_to_play(); 
  }
  # $selected_track == -1 when there is none selected track to play
  if ($selected_track == -1){
      if ($loop_tracks eq $false){
	 $status_bar->push($context_id, " Please, select at least one track to play.");  
         $go_forward = $false;
         stop_playing_cda();
	 show_time();	 
         return $false;	
      }
      else {
         $force_unselect_all_tracks = $false; # to select all tracks
         unselect_cda();  # subroutine that select all tracks
         ($selected_track, $total_time) = track_to_play();
         @tracks_already_played = (); 
         @tracks_already_played_time = ();
         $selected_track_before = undef;
      }
  }
  my $audiodevide_path = $entry_cda->get_text(); # /dev/hdc or /dev/cdrom or ...

  if ( not $normalize_button->get_label =~ /normaliz/ ) { #if ripping, not begin to play
     $status_bar->push($context_id, " Not play while ripping!");
     return; 
  } 
  $status_bar->push($context_id, ""); 
  
  if ( $selected_track != -1 ){ 
     
     # find $iter and $path associeted to some $track 
     my ($iter2,$path) = search_iter_path_for_track($selected_track);   
     my $row = $selected_track -1;
     $treeview4->scroll_to_cell ($path, undef, $false ,0.0, 0.0);
     my $treeselection = $treeview4->get_selection;  # see <man Gtk2::TreeSelection>
     $treeselection->select_path ($path);
     if ( defined($selected_track_before) ){ # select the last played track	
	my ($iter3,$path_before) = search_iter_path_for_track($selected_track_before); 
	$treeview4->set_drag_dest_row ($path_before, 'into-or-before');
     }
     while (Gtk2->events_pending()) {Gtk2->main_iteration()};
     
     #print " selected_track = $selected_track ;;iter = ",$model->get_value($iter2),"\n";
     push @tracks_already_played, $selected_track; 
     push @tracks_already_played_time, $total_time; 
     #print "--> tracks_already_played = @tracks_already_played  \n";
     
     # lame --quiet --decode FILENAME.mp3 - | play -t wav -  # play mp3 with lame
     #my $cmd = "$cdda2wav_path -D $audiodevide_path -q -e -t $selected_track -d0 -N ";
     #my $pid = open( PLAY, encode_char("nice -+$priority $cmd ") ) ;
     #close(PLAY);
     #$pid = fork;	
     
     # see <man SDL> or <man SDL::Cdrom> 
     #use SDL::Cdrom;
     #my $cdrom = SDL::Cdrom->new(0);
     #$cdrom->play();
     
     if ($cdcd_path ne "") {
        my $cmd = "$cdcd_path -d $audiodevide_path play $selected_track $selected_track ";
        exec_cmd_system($cmd);
	# print "using cdcd to play cdrom \n";
     }
     elsif ( $use_audiocd eq $true ){ # libcdaudio  ;; Audio::CD ;; see <man Audio::CD>
	my $cd = Audio::CD->init("$audiodevide_path");
        $cd->play_track($selected_track, $selected_track);
     }
     else{         
        my $cmd = "$cdplay_path -c $audiodevide_path play $selected_track ";
        exec_cmd_system($cmd);
     }
     
     $playing_audio_cd = $true;
     while (Gtk2->events_pending()) {Gtk2->main_iteration()};

     # see <man Glib::MainLoop>  
     # integer = Glib::Timeout->add ($interval, $callback, $data=undef, $priority=G_PRIORITY_DEFAULT)
     $timer = Glib::Timeout->add (250, \&change_count, $total_time); # 250 milliseconds = 0.25 second
     # integer = Glib::IO->add_watch ($fd, $condition, $callback, $data=undef, $priority=G_PRIORITY_DEFAULT)
  } 
  return $true;
} 

# Make array with all selected tracks that will be played
# It return the selected_track number and its time.
sub track_to_play {
       my $time = 0;
       my $random_element = -1;
       @tracks_to_play = ();
              
       my $model = $treeview4->get_model;
       for (my $row=0; $row <= $#music_cda ; $row++){
           my $track = $music_cda[$row]{track};              
           if ($music_cda[$row]{rip} eq $true ){  # if the track is toggled, then push it to array
	       push @tracks_to_play, $track; 
           }	   
       }
       #print " tracks = @tracks_to_play\n";
       # remove one element from front of @array -- see perlfunc : splice
       if ( @tracks_to_play >= 1 ){  # exist at least one element
	   if ( $play_random eq $true ){ 
	      my $rand = rand(@tracks_to_play); # pick one random element
	      # print " rand[1---N] = $rnd \n";
	      $random_element = splice(@tracks_to_play, int( $rand ), 1); # pull one element of @array
	   }
	   else { $random_element = splice(@tracks_to_play, 0, 1);} # pull the first element of @array
       }
       else{ return ( $random_element, $time ); 
       }
       $time = $music_cda[$random_element-1]{length}; # 2:56:34   02:32
       $time = time_to_sec($time);	
	
       #print " random_element = $random_element ;; time = $time ;; tracks_to_play = @tracks_to_play\n";
       return ( $random_element, $time );
}

# This is a loop for  "$timer = Glib::Timeout->add (250, \&change_count, $total_time);"
# at sub play_selection the show the time when button "play" is pressed.
sub change_count { 
  my $total_sec = shift; 
  show_time();
    
  # boolean = Glib::Source->remove ($tag)
  #   return $false;  # to stop Timeout

  # play all tracks  - go to next track
  if ( $count >= ($total_sec - 0) or $go_forward eq $true ) { # 0 second of correction
     stop_playing_cda();
     
     # find $iter and $path associeted to some $track
     my $model = $treeview4->get_model;
     my ($iter,$path) = search_iter_path_for_track($selected_track);    
     my $row = $selected_track -1;
     $music_cda[$row]{rip} = $false; # change the boolean value
     $model->set ($iter, COLUMN_RIP, $music_cda[$row]{rip}); # 'untaggle' the played music
     
     play_selection($selected_track); # if the track was played completely, pass it to $selected_track_before
     $go_forward = $false;
  }
  if ( $selected_track != -1 and $count % 10 == 0 and  100*$count % 1000 == 0 ){ 
     # find $iter and $path associeted to some $track
     my ($iter,$path) = search_iter_path_for_track($selected_track); 
     my $treeselection = $treeview4->get_selection;  # see <man Gtk2::TreeSelection>
     #$treeselection->select_path ($path);
     $treeselection->select_iter ($iter); # select this row at every 10 seconds.
  } 
  if ( $go_back eq $true and @tracks_already_played > 1 ) {  # ( 1 2 )
     stop_playing_cda();
     
     #print "antes -- tracks_already_played = @tracks_already_played  \n";
     ($selected_track, undef) = splice(@tracks_already_played, -2);  # remove the last two elements of @array
     ($total_time, undef) = splice(@tracks_already_played_time, -2); # undef is the last element
     #print "depois -- tracks_already_played = @tracks_already_played ;; selected_track = $selected_track\n";
         
     # find $iter and $path associeted to some $track
     my $model = $treeview4->get_model;
     my ($iter,$path) = search_iter_path_for_track($selected_track);    
     my $row = $selected_track -1;
     $music_cda[$row]{rip} = $true; # change the boolean value
     $model->set ($iter, COLUMN_RIP, $music_cda[$row]{rip}); # 'untaggle' the played music
     
     play_selection($selected_track); # with the track played completely ;; $selected_track_before       
  } 
  $go_back = $false;
   
  if ( $pause eq $false ){
     $count = $count + 0.25; # count is a number for seconds elapsed.
  }
  return $true;
}

# find $iter and $path associeted to some $track
# this is necessary when the column Track or Length is reordenable or altered
sub search_iter_path_for_track { 
   my $track = shift;
   my $iter;
   my $path;
   my $model = $treeview4->get_model;
   for (my $row=0; $row <= $#music_cda ; $row++){
       $path = Gtk2::TreePath->new ($row);
       $iter = $model->get_iter($path);
       my $track_f = $model->get_value($iter,COLUMN_TRACK); # track number   
                
       if ($track == $track_f){  # if the tracks are equals, the iter is right
          #print "track = $track ;; iter = ",$model->get_value($iter),"\n";
	  return ($iter,$path);   
       }	   
   }  
}

# show time on display
sub show_time{
  my $time;  
  my $count2 = int($count); # get the integer value
  
  $time = $total_time;
  
  if ( 1 == 2 ){  # not used
  if ($cdcd_path ne "" ) { 
     my $cmd = "$cdcd_path -d $audiodevide_path info";
     my $info = exec_cmd_system($cmd);     
     if ( $info =~ /Playing:.*\s((\d+:)?\d+:\d+)\sof\s((\d+:)?\d+:\d+)/g ){
        $count2 = time_to_sec($1);
     }
     $cmd = "$cdcd_path -d $audiodevide_path status";
     if ($info =~ /Stopped/i){ $go_forward = $true;}     
  }
  elsif ( $use_audiocd eq $true ){ # libcdaudio  ;; Audio::CD ;; see <man Audio::CD>
     my $cd = Audio::CD->init("$audiodevide_path");
     my $info = $cd->stat;
     my($minutes, $seconds) = $info->track_time;
     $count2 = time_to_sec("$minutes:$seconds");
  } 
  }
  my ($hour,$min,$sec) = sec_to_time($count2);
  my ($hour_r,$min_r,$sec_r) = sec_to_time($time - $count2); # time remaining
  my ($hour_t,$min_t,$sec_t) = sec_to_time($time);           # total time
      
  # print "--> hour = $hour ;; min = $min ;; sec = $sec ;; total = $total_sec ;; count = $count\n"; 
  if ( $show_time_remaining eq $false ){
     if ( $hour_r == 0 ){ draw_layout($da, undef, "$min:$sec/$min_t:$sec_t"); }
     else { draw_layout($da, undef, "$hour:$min:$sec/$hour_t:$min_t:$sec_t"); }     
  }
  else{
     if ( $hour_r == 0 ){ draw_layout($da, undef, "$min_r:$sec_r/$min_t:$sec_t"); }
     else { draw_layout($da, undef, "$hour_r:$min_r:$sec_r/$hour_t:$min_t:$sec_t");}
  }
  # Returns the current track play time:  # not used
  if ( $use_audiocd eq $true and 1==2 ){ 
     my $cd = Audio::CD->init("$audiodevide_path"); # see <man Audio::CD>
     my $info = $cd->stat;
     ($min, $sec) = $info->track_time;
     $min = sprintf("%02d", $min); #leading zero
     $sec = sprintf("%02d", $sec); 
     draw_layout($da, undef, "$min:$sec"); 
     return;
  }
    
  return $true;
}

sub stop_playing_cda{
   my $cmd;
   
   # update only if audio cd is not playing
   if ( $playing_audio_cd eq $false ){ 
      $audiodevide_path = $entry_cda->get_text(); # /dev/hdc or /dev/cdrom or ...
   }
   if (defined($timer)){Glib::Source->remove($timer);} #remove playing time = min:sec
   if ( $cdplay_path eq "" and $cdcd_path eq "" and  $use_audiocd eq $false){ return; }
   
   # if the file not exist or is a directory
   if (not -e $audiodevide_path or -d $audiodevide_path ) { return; }
     
   # this is necessary because cdplay crash if the drive is empty.
   $cmd = "mount $audiodevide_path ";
   my $list = exec_cmd_system($cmd);
   if ( $list =~ /No medium found/i ){ return ;}  
   
   if ($go_forward eq $false and $go_back eq $false and $playing_audio_cd eq $true){
      if ($cdcd_path ne "") {
         my $cmd = "$cdcd_path -d $audiodevide_path stop ";
         exec_cmd_system($cmd);
      }
      elsif ( $use_audiocd eq $true ){ # libcdaudio  ;; Audio::CD ;; see <man Audio::CD>
         my $cd = Audio::CD->init("$audiodevide_path");
         $cd->stop;
      }
      else{         
         $cmd = "$cdplay_path -c $audiodevide_path stop ";
         exec_cmd_system($cmd);
      }
      $playing_audio_cd = $false;
   }        
   $count = 0;     
}

# convert x seconds to hour:min:sec format
sub sec_to_time{
  my $i = shift;
  my $hour = int($i/3600);
  my $remainder =  $i % 3600; # see <man perlop>
  # $remainder = $i - $hour * 3600;
  my $min = sprintf("%02d", int($remainder/60) );  # 02d : leading zero
  my $sec = sprintf("%02d", $i - 60*$min );  
  return ($hour,$min,$sec);
}

# convert hour:min:sec format to  x seconds.
sub time_to_sec{    # $time = 2:34:01
  my $time = shift;
  ( my $sec = $time) =~ s/.*://g;
  $time =~ s/:\d+//g;
  ( my $min = $time) =~ s/.*://g;  
  if ( $min eq ""  ){$min = 0; }
  $time =~ s/(\d+:)\d+/$1/g;
  my $hour = 0;
  if ( $time =~ /:/ ) { ( $hour = $time) =~ s/(\d+):/$1/g; }
  $time = $hour * 3600 + $min * 60 + $sec;
  return $time;	  
}

sub rip_audio_cdparanoia {
        my $track = shift;
	my $file_wav = shift;
	my $row = $track -1;
	#Ripping from sector       0 (track  1 [0:00.00])
        #          to sector   18511 (track  1 [4:06.61])
	
	my $opt = "";
	if ($button_noia1->get_active){ $opt .= "-Z ";}
	if ($button_noia2->get_active){ $opt .= "-z ";}
	if ($button_noia3->get_active){ $opt .= "-Y ";}
	if ($button_noia4->get_active){ $opt .= "-X ";}
		
	my $cmd = "$cdparanoia_path -d $audiodevide_path -e -w $opt  $track \"$directory/$file_wav\" ";

	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview 
	insert_msg("\n" , "small");

	my $pid = open( NOIA, encode_char("nice -+$priority $cmd  2>&1 |") ) ;
	if( !defined($pid) )
	{
		$status_bar->push($context_id, " Could not rip!");
		print ( "Could not rip!  ");
		return $false;
	}
	$pid_rip_cda = $pid;	
	
	$status_bar->push($context_id, " ripping audio to wav");
	$pbar->set_text((reduce_length_size("$track-$music_cda[$track-1]{title}")));
	draw_input_output($da_arrow_dec,undef,("cda","wav")); 
	
	my $div = 0; # initial value
	my $saved_value = 0;
	while( <NOIA> ){	
	        #print " fora $_\n";
	        # simple output:
		#outputting to track02.cdda.wav
		##: -2 [wrote] @ 682079
                ##: -2 [wrote] @ 683255
                ##: -2 [wrote] @ 684431
		#Ripping from sector       0 (track  1 [0:00.00])
                #          to sector   18511 (track  1 [4:06.61])
                # (== PROGRESS == [ >                            .| 019261 00 ] == :^D . ==)
		
		if( $_ =~ /\[wrote\]\s+@\s+(\d+)/ )
		{ 
		  $saved_value = $div;
		  # progress = (sector-start)/(end-start);
		  my $sector = $1/1176;  # sector divide by 1176
		  $div = sprintf("%0.2f", ( $sector - $first_sector[$row] )/$music_sector[$row] ); 
		  $div = number_value($div);     
                  #                            show only not repeated value
		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ){ 
		     $pbar->set_fraction( $div ) ; 
		     my $pct = sprintf("%2d", 100*$div );
		     $status_bar->push($context_id, " ripping audio to wav - $pct%");
		     #print ( " div  = $div ;; $1 / 1176 ;; start = $first_sector[$row] ;; music_sector[$row] = $music_sector[$row]\n");
		  }		  
		}
		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
		if ( not $normalize_button->get_active and $pid_rip_cda >= 0 ) { 
		    $pid_rip_cda = StopProcessing($pid_rip_cda," Stop ripping!");
		    # remove wav file
		    exec_cmd_system("rm -f \"$directory/$file_wav\" ");
		    return $false;
		}
	}
	close( NOIA );
	if ($normalize_button->get_active) {  # if normalize button is active
	    $pbar->set_fraction( 1 ) ; 
	    $pid_rip_cda = -1; # reset rip pid on the final of process
	    return $true;
	}        
}  


sub rip_audio_cdda2wav {
        my $track = shift;
	my $file_wav = shift;
	my $audiodevide_path = $entry_cda->get_text(); # /dev/hdc or /dev/cdrom or ...
	my $row = $track-1;
	
	my $opt = "";
	if ($button_cdda1->get_active){ $opt .= "-paranoia ";}
	if ($button_cdda2->get_active){ $opt .= "-paraopts=disable ";}
	if ($button_cdda3->get_active){ $opt .= "-paraopts=no-verify ";}	
		
	# cdda2wav -g -no-infofile   dev=/dev/hdc -t 2 track  - see cdda2wav -help or man cdda2wav
	my $cmd = "$cdda2wav_path dev=$audiodevide_path -x -g -no-infofile $opt -t $track \"$directory/$file_wav\" ";
	
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview 
	insert_msg("\n" , "small");
	
	## $pid = pid of rip
	my $pid = open( CDRIP, encode_char("nice -+$priority $cmd 2>&1 |") );
	#$pid = fork; # man perlipc
	if( !defined($pid) )
	{
		$status_bar->push($context_id, " Could not rip!");
		print ( "Could not rip!  ");
		return $false;
	}	
	
	## set rip pid, pid > 0
	$pid_rip_cda = $pid;
	
	$status_bar->push($context_id, " ripping audio to wav");
	$pbar->set_text((reduce_length_size("$track-$music_cda[$row]{title}")));
	
	my $div = 0; # initial value
	my $saved_value = 0;

        ## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <CDRIP> ){	
	        # simple output:
		# 3%
		
		if( $_ =~ /\s+(\d+)%/g )
		{
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);      
		  #                            show only not repeated value
		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) { 
		     $pbar->set_fraction( $div ) ; 
		     $status_bar->push($context_id, " ripping audio to wav - $1%");
		     #print ( " div  = $div \n");
		  }		  
		}
		if( $_ =~ /100%.*successfully/i ){$pbar->set_fraction(1);}
		
		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
		if ( not $normalize_button->get_active and $pid_rip_cda >= 0 ) { 
		    $pid_rip_cda = StopProcessing($pid_rip_cda," Stop ripping!");
		    # remove wav file
		    exec_cmd_system("rm -f \"$directory/$file_wav\" ");
		    $/="\n";  # abandon loop
		    return $false;
		}
	}
	$/="\n"; ## final of loop
	close( CDRIP );
	
	if ($normalize_button->get_active) {  # if normalize button is active
	    $pbar->set_fraction(1) ; 
	    $pid_rip_cda = -1; # reset rip pid on the final of process
	    return $true;
	}        
}	

##-----------------------------------------------------##
##-----------------------------------------------------##

#  flac --> wav 

sub decode_flac_to_wav
{      
	#my $flac_path = $entry_ape->get_text();
	
	my $cmd = "$flac_path -d \"$directory/$file_flac\" -f -o \"$directory/$file_wav\" ";

	#print "\n$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");
	
	my $pid = open( FLACDEC, encode_char("nice -+$priority $cmd 2>&1 |") );
	if( !defined($pid) )
	{
		$status_bar->push($context_id, " Could not decode!");
		print ( "Could not decode!  ");
		return $false;
	}

	## set flac pid, pid > 0
	$pid_flac_decode = $pid;
	
	my $div = 0; # initial value
	my $saved_value = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <FLACDEC> )
	{	
	        #output:
		# 10-Que  feito de voc.flac: 75% complete

		if( $_ =~ /.*:\s+(\d+)%\scomplete/ )
		{
		  #print "\n<<>> $_ --->1=$1 \n";
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);
		  if ( $div > 1){ $div = 1;}  # to avoid possible errors

                  #                            show only not repeated value
		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) { 
		  $pbar->set_fraction( $div ) ; 
		  $status_bar->push($context_id, " flac decoding $1%");
		  #print ( " div  = $div \n");
		  }		  
		}
		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
		if ( not $normalize_button->get_active and $pid_flac_decode >= 0 ) { 
		    $pid_flac_decode = StopProcessing($pid_flac_decode," Stop flac decoding!");
		    # remove wav file
		    exec_cmd_system("rm -f \"$directory/$file_wav\" ");
		    $/="\n";  # abandon loop
		    return $false;
		}		
	}
	$/="\n"; ## final of loop
	close( FLACDEC );

	if ($normalize_button->get_active ) {  # if normalize button is active
	    $pbar->set_fraction( 1 ) ; 
	    $pid_flac_decode = -1;       # reset ape deocde pid on the final of process
	    return $true;
	}
}

#  ape --> wav  # dependence: mac from APE-3.96b8-3plf-MonkeyAudio.i586.rpm

sub decode_ape_to_wav
{      
	#my $ape_path = $entry_ape->get_text();
	my $cmd = "$ape_path  \"$directory/$file_ape\" \"$directory/$file_wav\" -d ";

	#print "\n$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");
	
	# STDIN is file descriptor number 0; STDOUT number 1; and STDERR number 2. 
	# To exchange a command's STDOUT and STDERR, i.e., capture the STDERR 
	# but have its STDOUT come out on our old STDERR:  3>&1 1>&2 2>&3 3>&-
	# 3>&1 : Make a new file descriptor, number 3, be a copy of number 1.
	# d3=d1
	# d1=d2
	# d2=d3
	my $pid = open( APEDEC, encode_char("nice -+$priority $cmd 3>&1 1>&2 2>&3 3>&- |") );
	if( !defined($pid) )
	{
		$status_bar->push($context_id, " Could not decode!");
		print ( "Could not decode!  ");
		return $false;
	}

	## set ape pid, pid > 0
	$pid_ape = $pid;
	
	my $div = 0; # initial value
	my $saved_value = 0;
	
	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <APEDEC> )
	{	
	        #output:
		#--- Monkey's Audio Console Front End (v3.96b8) (c) Matthew T. Ashland ---
                #Decompressing ...
                #Progress:  3.0% (357.7 sec remaining, 368.8 sec total)

		if( $_ =~ /^Progress:\s+(\d+[\.,]\d)%\s\((.*remaining).*/i )
		{
		  #print "\n<<>> $_ --->1=$1  --->2=$2\n";
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);
		  if ( $div > 1){ $div = 1;}  # to avoid possible errors

                  #                            show only not repeated value
		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) { 
		  $pbar->set_fraction( $div ) ; 
		  $status_bar->push($context_id, " ape decoding $1% | $2");
		  #print ( " div  = $div \n");
		  }		  
		}
		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
		if ( not $normalize_button->get_active and $pid_ape >= 0 ) { 
		    $pid_ape = StopProcessing($pid_ape," Stop ape decoding!");
		    # remove wav file
		    exec_cmd_system("rm -f \"$directory/$file_wav\" ");
		    $/="\n";  # abandon loop
		    return $false;
		}		
	}
	$/="\n"; ## final of loop
	close( APEDEC );

	if ($normalize_button->get_active ) {  # if normalize button is active
	    $pbar->set_fraction( 1 ) ; 
	    $pid_ape = -1;        # reset ape deocde pid on the final of process
	    return $true;
	}
}

#  mpc --> wav

sub decode_mpc_to_wav
{      	
	my $cmd = "$mpcdec_path  \"$directory/$file_mpc\" --wav \"$directory/$file_wav\" ";

	#print "\n$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");

	## $res = pid of ogg
	my $res = open( MPCDEC, encode_char("nice -+$priority $cmd 2>&1 |") );
	if( !defined($res) )
	{
		$status_bar->push($context_id, " Could not decode!");
		print ( "Could not decode!  ");
		return $false;
	}

	## set ogg pid, pid > 0
	$pid_mpc_decode = $res;
	
	my $div = 0; # initial value
	my $saved_value = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <MPCDEC> )
	{	
	        #output:		
		# 203.6 kbps,    4:52.68, SV 7.0, Profile 'BrainDead' (Beta 1.14)
                #     1:20.58/    4:52.68 decoded (27.5%)

		if( $_ =~ /\sdecoded\s\((\s*\d*[\.,]\d)%\)/ )
		{
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);
		  if ( $div > 1){ $div = 1;}  # to avoid possible errors

                  #                            show only not repeated value
		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) { 
		  $pbar->set_fraction( $div ) ; 
		  $status_bar->push($context_id, " mpc decode $1%");
		  #print ( " div  = $div \n");
		  }		  
		}
		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
		
		if ( not $normalize_button->get_active and $pid_mpc_decode >= 0 ) { 
	            $pid_mpc_decode = StopProcessing($pid_mpc_decode," Stop mpc decoding!"); 
	            if ( -e encode_char("$directory/$file_wav") ){exec_cmd_system("rm -f \"$directory/$file_wav\" ");}
		    return $false;
		}
	}
	$/="\n"; ## final of loop
	close( MPCDEC );
	
	if ($normalize_button->get_active ) {  # if normalize button is active
	    $pbar->set_fraction( 1 ) ; 
	    $status_bar->push($context_id, " mpc decode 100%");
	    $pid_mpc_decode = -1;       # reset oggdec pid on the final of process
	    return $true;
	}
}

#  ogg --> wav

sub decode_ogg_to_wav
{      
	my $cmd = "$oggdec_path  \"$directory/$file_ogg\" -o \"$directory/$file_wav\" ";

	#print "\n$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");

	## $res = pid of ogg
	my $res = open( OGGDEC, encode_char("nice -+$priority $cmd 2>&1 |") );
	if( !defined($res) )
	{
		$status_bar->push($context_id, " Could not decode!");
		print ( "Could not decode!  ");
		return $false;
	}

	## set ogg pid, pid > 0
	$pid_ogg_decode = $res;
	
	my $div = 0; # initial value
	my $saved_value = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <OGGDEC> )
	{	
	        #output: 
		# OggDec 1.0
                # Decoding "lixo-o tes'a.ogg" to "lixo-o tes'a.wav"
		#          [  5.5%]
		#          [ 88.5%]
                #          [100.0%]
		
		if( $_ =~ /^\s*\[(\s*\d*)[\.,](\d)%\]/ )
		{
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);
		  if ( $div > 1){ $div = 1;}  # to avoid possible errors

                  #                            show only not repeated value
		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) { 
		  $pbar->set_fraction( $div ) ; 
		  $status_bar->push($context_id, " ogg decode $1%");
		  }		  
		}
		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
		
		if ( not $normalize_button->get_active and $pid_ogg_decode >= 0 ) { 
	            $pid_ogg_decode = StopProcessing($pid_ogg_decode," Stop ogg decoding!"); 
	            if ( -e encode_char("$directory/$file_wav") ){exec_cmd_system("rm -f \"$directory/$file_wav\" ");}
		    return $false;
		}
	}
	$/="\n"; ## final of loop
	close( OGGDEC );
	
	if ($normalize_button->get_active) {  # if normalize button is active
	    $pid_ogg_decode = -1;             # reset oggdec pid on the final of process
	    return $true;
	}
}


#  mp3 --> wav

sub decode_mp3_to_wav
{      
        my $cmd = "$lame_path --verbose --decode \"$directory/$file_mp3\" \"$directory/$file_wav\" ";

	#print "\n$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");
 
	my $pid = open( LAME, encode_char("nice -+$priority $cmd 2>&1 |") );
	
	## set lame pid, pid > 0  see <man perlipc>
	$pid_lame_decode = $pid;
	
	my $saved_value = 0;
	my $div = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <LAME> )
	{	
	        #output: 
                #ID3v2 found. Be aware that the ID3 tag is currently lost when transcoding.
                #input:  almir_sater-instrumental-01_corumb.mp3
                #        (44.1 kHz, 2 channels, MPEG-1 Layer III)
                #output: almir_sater-instrumental-01_corumb.mp3.wav  (16 bit, Microsoft WAVE)
                #skipping initial 1105 samples (encoder+decoder delay)
                #Frame#  2806/8937   160 kbps  L  R  

                # print ( "\nclaudio teste;   lamepid =$settings{lamepid}\n");
		#print (" $_ \n");
		
		if( $_ =~ /^Frame\#\s*(\d+)\/(\d+)\s*.*/ )
		{
		   my $frames = $1;
	           my $numframes = $2;
		   $saved_value = $div;
		   $div = sprintf("%0.2f", $1/$2 );
		   $div = number_value($div);
		   my $pct = sprintf("%2.0f", $div*100 );
		   if ( $div > 1){ $div = 1;}

		   if ( $pbar->get_fraction() <=1 and $div > $saved_value ) { 
		      $pbar->set_fraction( $div ) ; 
		      $status_bar->push($context_id, " lame decode $pct%");
		   }
		}	
		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
		
		if ( not $normalize_button->get_active and $pid_lame_decode >= 0 ) { 
		    # set lamepid to -1 after stop the process
	            $pid_lame_decode = StopProcessing($pid_lame_decode," Stop lame decoding!"); 
		    # In the case of stop decode process, always remove file_wav if it exist
	            # because the file_wav is not complete/entire
	            if ( -e encode_char("$directory/$file_wav") ){exec_cmd_system("rm -f \"$directory/$file_wav\" ");}
		    return $false;
		}		
	}
	$/="\n"; ## final of loop
	close( LAME );	

	if ($normalize_button->get_active) {  # if normalize button is active
	    $pid_lame_decode = -1;            # reset lame pid on the final of process
	    return $true;
	}
}


## normalize wav  - insensibility level to be implemented

sub normalize_wav
{       my $cmd;	
        $already_normalized = $false;
	# my $threshold = $spinner_sensi->get_value;
	my $n_type; # normalization type
	if ( $type eq "Fix" )  { my $value = $value/100;  $n_type = "-a $value" ; }
	if ( $type eq "Peak" ) { $n_type = "--peak" ;   }
	if ( $type eq "Mix" )  { $n_type = "-m" ;        }
	
	# criate a directory: "$directory/normalized"
	if ( not opendir(DIR, encode_char("$directory/normalized") ) ){
	      $cmd = "mkdir \"$directory/normalized\" "; # create the directory normalized 
              exec_cmd_system($cmd); 
	}
	$directory_final = "$directory/normalized"; # where the mp3 files will be saved
	
	# make a copy of wav file to normalize or encode
	$cmd = "cp -f \"$directory/$file_wav\"  \"$directory/$file_wav_copy\" ";
	insert_msg("\n$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");
	exec_cmd_system($cmd);
	
	#----------------------------------------------------------------------#
	if ( $type eq "None" ){                                                #
	    $pbar_n->set_text("Not normalize");                                #
	    insert_msg("This file will not be normalized!" , "small-red");     # 
	    insert_msg("\n" , "small");                                        #
	    $pid_normalize = -1;                                               #
            return;                                                            #
	}                                                                      #
	#----------------------------------------------------------------------#
	
	# normalize the $file_wav_copy    
	$cmd = "$normalize_path $n_type -v -T 0.2 \"$directory/$file_wav_copy\" ";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");

	## start normalization
	my $pid = open( NORMALIZE, encode_char("nice -+$priority $cmd 2>&1 |") ) or die "couldn't execute \"$cmd\": $!\n";
	if( !defined($pid) )
	{
		$status_bar->push($context_id, " Could not normalize!");
		return $false;
	}
        
	## set normalize pid
	$pid_normalize = $pid;	
	
	my $saved_value = 0;
	my $div = 0;
	my $sinal = "";
	$adjust = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <NORMALIZE> )
	{       
		#Computing levels...
                #almir_sater-instr  90% done, ETA 00:00:00 (batch  90% done, ETA 00:00:00) 
                #Level for almir_sater-instrumental-01_corumba.wav: -14.2786dBFS (0.0000dBFS peak)
                #Applying adjustment of 3.82dB to almir_sater-instrumental-01_corumba.wav...
		#Applying adjustment of -9.49dB to almir_sater-instrumental-03_vinheta_do_capeta.wav...
                #almir_sater-instr  36% done, ETA 00:00:02 (batch  36% done, ETA 00:00:02) 
		#almir_sater-instrumental-01_corumba.wav already normalized, not adjusting...
		
		if ( $_ =~ /.*adjustment\s*of\s(-?\d+.\d+)dB.*/ ){ 
		    $adjust = number_value($1);
		    # put a positive sinal if $1 > 0
		    if ( not $1 =~ /-/ ) {$sinal="+";} 
		} 
		
		if ( $_ =~ /.*batch\s*(\d+)\%\s*done,\s*ETA.*/ )
		{
		   $saved_value = $saved_value +1;
		   #print ( "div = $1  ; saved_value = $saved_value; adjust = $adjust ; a_n = $already_normalized\n");
		   $div = number_value(sprintf("%0.2f", $1/100 ));

		   if ( $pbar_n->get_fraction() <=1 and $saved_value <= 100 ){ 
		      $pbar_n->set_fraction( $div ) ; 
		      #print ( "div = $div \n");
		      $pbar_n->set_text("Computing levels...");
		      $pbar_n->set_orientation('right_to_left');
		   }
		   if ( $pbar_n->get_fraction() <=1 and $saved_value > 100 ){ 
		      $pbar_n->set_fraction( $div ) ;  
		      $pbar_n->set_text("Applying adjustment of ${sinal}${adjust}dB...");
		      $status_bar->push($context_id, " normalizing: adjustment of ${sinal}${adjust}dB");
		      $pbar_n->set_orientation('left_to_right');
		   }
		}
		if ( $_ =~ /.*\s*already\s*normalized.*/ ){		  
		   $already_normalized = 1;
		   $pbar_n->set_fraction( 1 ) ; 
		   $pbar_n->set_text(" already normalized ");	      
		   insert_msg("This file is already normalized.\n" , "small-red");
		}
		while (Gtk2->events_pending()) { Gtk2->main_iteration()};
		if ( not $normalize_button->get_active and $pid_normalize >= 0 ) { 
		    $pid_normalize = StopProcessing($pid_normalize," Stop normalizing!");
		    $/="\n";  # abandon loop
		}
	}
	$/="\n"; ## final of loop
	close( NORMALIZE );	
    
	if ($normalize_button->get_active) {  # if normalize button is active
	    if ( not $already_normalized ){
	       insert_msg(" normalized with adjustment of ${sinal}${adjust}dB." , "black");
	       insert_msg("\n" , "small");
	    }
	    $pid_normalize = -1;         # reset normalize pid on the final of process
	    return $true;
	}
	else {return $false;}    	
}

# wav --> ogg
sub encode_wav_to_ogg
{      
	my $comment;
	my $cmd; # command	
	my $bitrate;
	my $mode;
	my $frequency;
	
        ($encode,$mode,$bitrate,$frequency,$vb_Min,$vb_Max) = encode_settings();   
	
	# tag comment
	if ( $type eq "Fix" ){
	       $comment = "gnormalize $type value=$value";}
	else { $comment = "gnormalize $type ";}	
	
	#$comment .= $ogg_comment; # add to comment  ---  tag commands for normalized ogg file
	my $cmd_tag = " -t \"$mp3_title\" -a \"$mp3_artist\"  -l \"$mp3_album\" ";
	$cmd_tag .= " -d \"$mp3_year\" -c \"comment=$comment\" -G \"$mp3_genre\" ";
	
	if ( $total_track ne "" and $total_track >= $mp3_tn ){ $cmd_tag .= " -N \"$mp3_tn/$total_track\" "; }	
	else { $cmd_tag .= " -N \"$mp3_tn\" "; }
	
	# oggenc commands
	if ( $encode eq "variable" ){ # VBR (variable bitrate)
	   # b - minimum allowed bitrate - see  <man oggenc>
	   # B - maximum allowed bitrate
	   # q - Variable  BitRate -  -1 <= quality <= 10
	   $cmd = "$oggenc_path -q $Vogg -m $vb_Min -M $vb_Max ";
        }
	else{  # for ABR (average bitrate)
	   $cmd = "$oggenc_path -b $bitrate -m $vb_Min -M $vb_Max "; 
	}   
	# common commands of oggenc for all options of encode
	$cmd .= " $cmd_tag --resample \"$frequency\" ";
	$cmd .= " \"$directory/$file_wav_copy\" -o \"$directory_final/$file_ogg\" ";
	
	#print "$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");
	
	## start encoding
	my $res = open( OGGENC, encode_char("nice -+$priority $cmd 2>&1 |") ) or die "couldn't execute \"$cmd\": $!\n";
        ## pid process of oggenc 
	if( !defined($res) )
	{
		$status_bar->push($context_id, " Could not encode!");
		return $false;
	}      
	
	## set ogg encode pid
	$pid_ogg_encode = $res;
	
	my $saved_value = 0;
	my $div = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <OGGENC> )
	{  
		## this is output of oggenc:
		
		# Opening with wav module: WAV file reader
                # Encoding "music.wav" to
                #          "music.ogg"
                # at quality 3.00   
		#        [  1.5%] [ 0m13s remaining] |
                #        [ 21.6%] [ 0m08s remaining] /
		#        [ 99.9%] [ 0m00s remaining] -
		# Done encoding file "music.ogg"
                #        File length:  0m 56.0s
                #        Elapsed time: 0m 10.7s
                #        Rate:         5.3075
                #        Average bitrate: 114.4 kb/s
		
		if( $_ =~ /^\s*\[(\s*\d*[\.,]\d)%\]\s+\[\s*(\d*)m(\d*)s\sremaining\]/ )
		{
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);
		  my $pct = sprintf("%02d", $1 ); # 02d : leading zero
		  if ( $div > 1){ $div = 1;}  # to avoid possible errors

                  #                            show only not repeated value
		  if ( $pbar_lame->get_fraction() <=1 and $div > $saved_value ) { 
		  $pbar_lame->set_fraction( $div ) ; 
		  $status_bar->push($context_id, " ogg encode $pct% | time remaining = $2:$3");
		  }		  
		}		
		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
		
		if ( not $normalize_button->get_active and $pid_ogg_encode >= 0 ) { 
		    $pid_ogg_encode = StopProcessing($pid_ogg_encode," Stop ogg encoding!");
		    $/="\n";  # abandon loop
		}
	}
	$/="\n"; ## final of loop
	close(OGGENC);
	
	finalize_process($file_ogg); 
	
	if ($normalize_button->get_active) {  # if normalize button is active
	    $pid_ogg_encode = -1;             # reset pid on the final of process  
            return $true;    }
	else { return $false;}
}


# wav --> mpc
sub encode_wav_to_mpc
{      
	my $comment;
	my $cmd; # command
	my $bitrate;
	my $mode;
	my $frequency;
	
        ($encode,$mode,$bitrate,$frequency,$vb_Min,$vb_Max) = encode_settings();
	
	# tag comment
	if ( $type eq "Fix" ){
	       $comment = "gnormalize $type value=$value";}
	else { $comment = "gnormalize $type ";}	
	
	# tag of mpc file
	if ($mp3_title eq ""){($mp3_title = $file_mpc) =~ s/\.(.){3}$//;}
	# bug of xmms plugin for mpc files: the tag must have at 
	# last a empty space, example: --artist " "
	if ($mp3_artist eq ""){$mp3_artist = " ";}
	if ($mp3_album eq ""){$mp3_album = " ";}
	if ($mp3_title eq ""){$mp3_title = " ";}
	if ($mp3_year eq ""){$mp3_year = " ";}
	if ($mp3_comment eq ""){$mp3_comment = " ";}
	if ($mp3_genre eq ""){$mp3_genre = " ";}
	if ($mp3_tn eq ""){$mp3_tn = " ";}
	
	my $cmd_tag = " --artist \"$mp3_artist\" --album \"$mp3_album\"  --tag \"Title=$mp3_title\" ";
	$cmd_tag .= " --tag \"Year=$mp3_year\" --tag \"Comment=$comment\" --tag \"Genre=$mp3_genre\" ";
	
	if ( $total_track ne "" and $total_track >= $mp3_tn ) {
               $cmd_tag .= " --tag \"Track=$mp3_tn/$total_track\"";}
        else { $cmd_tag .= " --tag \"Track=$mp3_tn\"";} 

	# --nmt x     set NMT value to x dB (dflt:  6.5)
	$cmd = "$mpcenc_path --overwrite --quality $Vmpc ";
		
	# common commands of mpcenc for all options of encode
	$cmd .= " $cmd_tag ";
	$cmd .= " \"$directory/$file_wav_copy\"  \"$directory_final/$file_mpc\" ";
	
	#print "$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview 
	insert_msg("\n" , "small");
	
	## start encoding
	my $res = open( MPCENC, encode_char("nice -+$priority $cmd 2>&1 |") ) or die "couldn't execute \"$cmd\": $!\n";
        ## pid process of mpcenc 
	if( !defined($res) )
	{
		$status_bar->push($context_id, " Could not encode!");
		return $false;
	}      
	
	## set ogg encode pid
	$pid_mpc_encode = $res;
	
	my $saved_value = 0;
	my $div = 0;
	
	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <MPCENC> )
	{  
		## this is output of mppenc:
		#    %|avg.bitrate| speed|play time (proc/tot)| CPU time (proc/tot)| ETA
                #  5.9  119.8 kbps  7.67x     0:17.1    4:52.6     0:02.2    0:38.1     0:35.9
		# 24.4  126.3 kbps  7.49x     1:11.5    4:52.6     0:09.5    0:39.0     0:29.5
                # 24,4
		
		if( $_ =~ /^\s+(\d+[\.,]\d+)\s+.*kbps\s+(\d+[\.,]\d+)x.*(\d+:\d+)/ )
		{ 
		  #print "\n<<>> $_ --->1=$1  --->2=$2  --->3=$3\n";
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);
		  my $pct = sprintf("%02d", $1 ); # 02d : leading zero
		  if ( $div > 1){ $div = 1;}  # to avoid possible errors

                  #                            show only not repeated value
		  if ( $pbar_lame->get_fraction() <=1 and $div > $saved_value ) { 
		  $pbar_lame->set_fraction( $div ) ; 
		  # print ( " div  = $div  ; time remain = $3 \n");
		  $status_bar->push($context_id, " mpc encode $pct% | time remaining = $3 | encode speed = $2");
		  }		  
		}
		while (Gtk2->events_pending()) { Gtk2->main_iteration()};
		
		if ( not $normalize_button->get_active and $pid_mpc_encode >= 0 ) { 
		    $pid_mpc_encode = StopProcessing($pid_mpc_encode," Stop mpc encoding!");
		    $/="\n";  # abandon loop
		}
	}
	$/="\n"; ## final of loop
	close(MPCENC);
	
	finalize_process($file_mpc); 
	
	if ($normalize_button->get_active) {  # if normalize button is active
	    $pid_mpc_encode = -1;             # reset pid on the final of process  
            return $true;    }
	else { return $false;}
}


# wav --> ape
sub encode_wav_to_ape {    
	my $compress = $spinner_compress->get_value_as_int();

	# tag comment
	if ( $type eq "Fix" ){
	       $mp3_comment = "gnormalize $type value=$value";}
	else { $mp3_comment = "gnormalize $type ";}	
	
	my $cmd = "$ape_path \"$directory/$file_wav_copy\"  \"$directory_final/$file_ape\"  -c$compress ";

	#print "\n$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");
	
	# To exchange a command's STDOUT and STDERR in order to capture the STDERR 
	my $pid = open( APEENC, encode_char("nice -+$priority $cmd 3>&1 1>&2 2>&3  |") );
	if( !defined($pid) )
	{
		$status_bar->push($context_id, " Could not decode!");
		print ( "Could not decode!  ");
		return $false;
	}

	## set ape pid, pid > 0
	$pid_ape = $pid;
	
	my $div = 0; # initial value
	my $saved_value = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <APEENC> )
	{	
	        #output:
		#--- Monkey's Audio Console Front End (v3.96b8) (c) Matthew T. Ashland ---
                #Decompressing ...
                #Progress:  3.0% (357.7 sec remaining, 368.8 sec total)

		if( $_ =~ /^Progress:\s+(\d+[\.,]\d)%\s\((.*)\)/ )
		{
		  #print "\n<<>> $_ --->1=$1  --->2=$2\n";
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);
		  if ( $div > 1){ $div = 1;}  # to avoid possible errors

                  #                            show only not repeated value
		  if ( $pbar_lame->get_fraction() <=1 and $div > $saved_value ) { 
		  $pbar_lame->set_fraction( $div ) ; 
		  $status_bar->push($context_id, " ape encoding $1% | $2");
		  #print ( " div  = $div \n");
		  }		  
		}
		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
		if ( not $normalize_button->get_active and $pid_ape >= 0 ) { 
		    $pid_ape = StopProcessing($pid_ape," Stop ape encoding!");
		    $/="\n";  # abandon loop
		}		
	}
	$/="\n"; ## final of loop
	close( APEENC );
	
	remove_apetag ("$directory_final/$file_ape");   	    
	write_apetag ("$directory_final/$file_ape");
	
	finalize_process($file_ape); 
	
	if ($normalize_button->get_active) {  # if normalize button is active
	    $pid_ape = -1;                    # reset pid on the final of process  
            return $true;    }
	else { return $false;}
}

#  wav --> flac 

sub encode_wav_to_flac {      
	my $comment;
	my $cmd; # command
	my $bitrate;
	my $mode;
	my $frequency;
	my $compress = $spinner_compress_flac->get_value();
	
	# tag comment
	if ( $type eq "Fix" ){
	       $comment = "gnormalize $type value=$value";}
	else { $comment = "gnormalize $type ";}	
	
        ($encode,$mode,$bitrate,$frequency,$vb_Min,$vb_Max) = encode_settings();

	$cmd = "$flac_path -$compress -f --tag=Comment=\"$comment\" \"$directory/$file_wav_copy\" -o \"$directory_final/$file_flac\" ";

	#print "\n$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview
	insert_msg("\n" , "small");
	
	my $pid = open( FLAC, encode_char("nice -+$priority $cmd 2>&1 |") );
	if( !defined($pid) )
	{
		$status_bar->push($context_id, " Could not decode!");
		print ( "Could not decode!  ");
		return $false;
	}

	## set flac pid, pid > 0
	$pid_flac_decode = $pid;
	
	my $div = 0; # initial value
	my $saved_value = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <FLAC> )
	{	
	        #output:options: -P 4096 -b 4608 -m -l 8 -q 0 -r 3,3
                #audio.wav: 51% complete, ratio=0.717

		if( $_ =~ /.*:\s+(\d+)%\scomplete/ )
		{
		  #print "\n<<>> $_ --->1=$1 \n";
		  $saved_value = $div;
		  $div = sprintf("%0.2f", $1/100 );
		  $div = number_value($div);
		  if ( $div > 1){ $div = 1;}  # to avoid possible errors

                  #                             show only not repeated value
		  if ( $pbar_lame->get_fraction() <=1 and $div > $saved_value ) { 
		  $pbar_lame->set_fraction( $div ) ; 
		  $status_bar->push($context_id, " flac encoding $1%");
		  }		  
		}
		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
		if ( not $normalize_button->get_active and $pid_flac_decode >= 0 ) { 
		    $pid_flac_decode = StopProcessing($pid_flac_decode," Stop flac encoding!");
		    $/="\n";  # abandon loop
		}		
	}
	$/="\n"; ## final of loop
	close(FLAC);		

	# tag commands -- to see the entries: metaflac --list music.flac
	# --no-utf8-convert
	$mp3_title = encode("utf8",$mp3_title);
	$mp3_artist = encode("utf8",$mp3_artist);
	$mp3_album = encode("utf8",$mp3_album);
	$mp3_genre = encode("utf8",$mp3_genre);
	
 	my $cmd_tag = "$metaflac_path --set-vc-field=Title=\"$mp3_title\" --set-vc-field=Artist=\"$mp3_artist\"";
	$cmd_tag .= " --set-vc-field=Album=\"$mp3_album\" --set-vc-field=Date=\"$mp3_year\"";
	# put the total_track in file_mp3
	if ($total_track ne "" and $total_track >= $mp3_tn){$cmd_tag .= " --set-vc-field=Tracknumber=\"$mp3_tn/$total_track\"";}
	else {$cmd_tag .= " --set-vc-field=Tracknumber=\"$mp3_tn\"";}
	$cmd_tag .= " --set-vc-field=Genre=\"$mp3_genre\"  \"$directory_final/$file_flac\"";	
		
	if ( $metaflac_path ne "" and $normalize_button->get_active ){
	   insert_msg("." , "small-ini");
	   insert_msg( decode("utf8",$cmd_tag) , "small"); #print on debug textview
	   insert_msg("\n" , "small");
	   exec_cmd_system($cmd_tag);	
	}
	
	finalize_process($file_flac); 
	 
	if ($normalize_button->get_active) {  # if normalize button is active
	    $pid_flac_decode = -1;            # reset pid on the final of process  
            return $true;    }
	else { return $false;}
}


# wav --> mp3
sub encode_wav_to_mp3
{      
	my $cmd; # command
	my $bitrate;
	my $mode;
	my $frequency;
	
        ($encode,$mode,$bitrate,$frequency,$vb_Min,$vb_Max) = encode_settings();
	
	# tag comment
	if ( $type eq "Fix" ){
	       $mp3_comment = "gnormalize $type value=$value";}
	else { $mp3_comment = "gnormalize $type ";}	
	
	# tag commands for normalized mp3 file 
	my $cmd_tag = " --ignore-tag-errors --add-id3v2 --tt \"$mp3_title\" ";
	$cmd_tag .= " --ta \"$mp3_artist\" --tl \"$mp3_album\" --ty \"$mp3_year\" ";
	$cmd_tag .= " --tc \"$mp3_comment\" --tg \"$mp3_genre\" ";	
	if ( $total_track ne "" and $total_track >= $mp3_tn ){ $cmd_tag .= " --tn \"$mp3_tn/$total_track\" "; }	
	else { $cmd_tag .= " --tn \"$mp3_tn\" "; }	
	
	# lame commands
	if ( $encode eq "constant" ){ # CBR (constant bitrate)
	   $cmd = "$lame_path -q $quality -c -b $bitrate ";
        }
	if ( $encode eq "average" ){ # ABR (average bitrate)
	   $cmd = "$lame_path -q $quality -c --abr $bitrate -b $vb_Min -B $vb_Max --nohist ";
        }
	if ( $encode eq "variable" and $check_button_change->get_active ){ # VBR (variable bitrate)
	   # b - minimum allowed bitrate - see  <man lame>
	   # B - maximum allowed bitrate
	   # V - Variable  BitRate -  0 <= quality <= 9
	   $cmd = "$lame_path -q $quality -c --vbr-new -V $V -b $vb_Min -B $vb_Max --athtype 2 --nohist ";
        }	
	# common commands of lame for all options of encode
	$cmd .= " $cmd_tag  -m \"$mode\"  --resample \"$frequency\" ";
	$cmd .= " \"$directory/$file_wav_copy\" \"$directory_final/$file_mp3\" ";
	
	#print "$cmd\n";
	insert_msg("." , "small-ini");
	insert_msg("$cmd" , "small"); #print on debug textview 
	insert_msg("\n" , "small");
	
	## start encoding
	my $res = open( LAME, encode_char("nice -+$priority $cmd 2>&1 |") ) or die "couldn't execute \"$cmd\": $!\n";
        ## pid process of lame 
	if( !defined($res) )
	{
		$status_bar->push($context_id, " Could not encode!");
		return $false;
	}      
	
	## set lame encode pid
	$pid_lame_encode = $res;
	
	my $div = 0;

	## loop for output - initial
	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
	while( <LAME> )
	{  
		## this is output of lame.
		#LAME version 3.96.1 (http://lame.sourceforge.net/)
                #CPU features: MMX (ASM used), 3DNow! (ASM used), SSE
                #Using polyphase lowpass filter, transition band: 17249 Hz - 17782 Hz
                #Encoding almir_sater-instrumental-03_vinheta_do_capeta.wav to lixo.mp3
                #Encoding as 44.1 kHz 128 kbps j-stereo MPEG-1 Layer III (11x) qval=3
                #   Frame          |  CPU time/estim | REAL time/estim | play/CPU |    ETA 
		#   320/2171   ( 7%)|    0:05/    0:09|    0:05/    0:10|   5.8102x|    0:04 
                #  1250/2171   (58%)|    0:05/    0:09|    0:05/    0:10|   5.8102x|    0:04 
		
		if( $_ =~ /^\s.*\s*\(\s*(\d+)%\)\|\s*.*\s+(\d+[\.,]\d+)x\|\s+(\d+\:\d+).*/ )
		{		  
		  $div = sprintf("%0.2f", $1/100 ); # eg (65%)/100
		  $div = number_value($div);
		  #print ( "div = $1 ; speed = $2 \n");
		  my $speed = sprintf("%2.2f", $2 );
		  my $time = $3;
		  my $pct = sprintf("%02d", $1 ); # 02d : leading zero

		  if ( $pbar_lame->get_fraction() <=1 and $pid_lame_encode>=0 ) { 
		     $pbar_lame->set_fraction( $div ) ; 
		     $status_bar->push($context_id, " lame encode $pct% | time remaining = $time | encode speed = $speed");
		  }
		}
		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
		
		if ( not $normalize_button->get_active and $pid_lame_encode >= 0 ) { 
		    $pid_lame_encode = StopProcessing($pid_lame_encode," Stop lame encoding!");
		    $/="\n";  # abandon loop
		}		
	}
	$/="\n"; ## final of loop
	close( LAME );
	
	# put the total_track in file_mp3 --- lame can not save 'track/total_track'
	if ( $total_track ne "" and $total_track >= $mp3_tn){ 
	    my $file = $directory_final."/".$file_mp3;
	    save_mp3_tag($file); 
	}		    
	finalize_process($file_mp3); 
	 
	if ($normalize_button->get_active) {  # if normalize button is active
	    $pid_lame_encode = -1;            # reset pid on the final of process  
            return $true;    }
	else { return $false;}
}

# Get values from config file ~/.gnormalize_config
if    ($file_output eq 'mp3'  and $lame_path ne ""  ) {$button_output_mp3->set_active( $true ); }  #padro - ativo
elsif ($file_output eq 'mpc'  and $mpcenc_path ne "") {$button_output_mpc->set_active( $true ); }
elsif ($file_output eq 'ogg'  and $oggenc_path ne "") {$button_output_ogg->set_active( $true ); }
elsif ($file_output eq 'ape'  and $ape_path ne ""   ) {$button_output_ape->set_active( $true ); }
elsif ($file_output eq 'flac' and $flac_path ne ""  ) {$button_output_flac->set_active( $true );}
encode_choice_sensitive();

# Quit of gnormalize
sub Quit { 
   stop_playing_cda();
   save_config();
   Gtk2->main_quit; 
   return $true; 
}

# Gtk event loop
Gtk2->main; # necessrio para executar o programa
exit(0);

















## Copyright (c) 1998-2003 - Chris Nandor  - <pudge@pobox.com>
## MP3::Info Project Page:   http://projects.pudge.net/

package MP3::Info;
use overload;
use strict;
use Carp;
use Symbol;

use vars qw(
	@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION $REVISION
	@mp3_genres %mp3_genres @winamp_genres %winamp_genres $try_harder
	@t_bitrate @t_sampling_freq @frequency_tbl %v1_tag_fields
	@v1_tag_names %v2_tag_names %v2_to_v1_names $AUTOLOAD
	@mp3_info_fields
);

@ISA = 'Exporter';
@EXPORT = qw(
	set_mp3tag get_mp3tag get_mp3info remove_mp3tag
	use_winamp_genres
);
@EXPORT_OK = qw(@mp3_genres %mp3_genres use_mp3_utf8);
%EXPORT_TAGS = (
	genres	=> [qw(@mp3_genres %mp3_genres)],
	utf8	=> [qw(use_mp3_utf8)],
	all	=> [@EXPORT, @EXPORT_OK]
);

# $Id: Info.pm,v 1.15 2003/03/02 19:16:43 pudge Exp $
($REVISION) = ' $Revision: 1.15 $ ' =~ /\$Revision:\s+([^\s]+)/;
$VERSION = '1.02';

=pod

=head1 NAME

MP3::Info - Manipulate / fetch info from MP3 audio files

=head1 SYNOPSIS

	#!perl -w
	use MP3::Info;
	my $file = 'Pearls_Before_Swine.mp3';
	set_mp3tag($file, 'Pearls Before Swine', q"77's",
		'Sticks and Stones', '1990',
		q"(c) 1990 77's LTD.", 'rock & roll');

	my $tag = get_mp3tag($file) or die "No TAG info";
	$tag->{GENRE} = 'rock';
	set_mp3tag($file, $tag);

	my $info = get_mp3info($file);
	printf "$file length is %d:%d\n", $info->{MM}, $info->{SS};

=cut

{
	my $c = -1;
	# set all lower-case and regular-cased versions of genres as keys
	# with index as value of each key
	%mp3_genres = map {($_, ++$c, lc, $c)} @mp3_genres;

	# do it again for winamp genres
	$c = -1;
	%winamp_genres = map {($_, ++$c, lc, $c)} @winamp_genres;
}

=pod

	my $mp3 = new MP3::Info $file;
	$mp3->title('Perls Before Swine');
	printf "$file length is %s, title is %s\n",
		$mp3->time, $mp3->title;


=head1 DESCRIPTION

=over 4

=item $mp3 = MP3::Info-E<gt>new(FILE)

OOP interface to the rest of the module.  The same keys
available via get_mp3info and get_mp3tag are available
via the returned object (using upper case or lower case;
but note that all-caps "VERSION" will return the module
version, not the MP3 version).

Passing a value to one of the methods will set the value
for that tag in the MP3 file, if applicable.

=cut

sub new {
	my($pack, $file) = @_;

	my $info = get_mp3info($file) or return undef;
	my $tags = get_mp3tag($file) || { map { ($_ => undef) } @v1_tag_names };
	my %self = (
		FILE		=> $file,
		TRY_HARDER	=> 0
	);

	@self{@mp3_info_fields, @v1_tag_names, 'file'} = (
		@{$info}{@mp3_info_fields},
		@{$tags}{@v1_tag_names},
		$file
	);

	return bless \%self, $pack;
}

sub can {
	my $self = shift;
	return $self->SUPER::can(@_) unless ref $self;
	my $name = uc shift;
	return sub { $self->$name(@_) } if exists $self->{$name};
	return undef;
}

sub AUTOLOAD {
	my($self) = @_;
	(my $name = uc $AUTOLOAD) =~ s/^.*://;

	if (exists $self->{$name}) {
		my $sub = exists $v1_tag_fields{$name}
			? sub {
				if (defined $_[1]) {
					$_[0]->{$name} = $_[1];
					set_mp3tag($_[0]->{FILE}, $_[0]);
				}
				return $_[0]->{$name};
			}
			: sub {
				return $_[0]->{$name}
			};

		no strict 'refs';
		*{$AUTOLOAD} = $sub;
		goto &$AUTOLOAD;

	} else {
		carp(sprintf "No method '$name' available in package %s.",
			__PACKAGE__);
	}
}

sub DESTROY {

}


=item use_mp3_utf8([STATUS])

Tells MP3::Info to (or not) return TAG info in UTF-8.
TRUE is 1, FALSE is 0.  Default is FALSE.

Will only be able to it on if Unicode::String is available.  ID3v2
tags will be converted to UTF-8 according to the encoding specified
in each tag; ID3v1 tags will be assumed Latin-1 and converted
to UTF-8.

Function returns status (TRUE/FALSE).  If no argument is supplied,
or an unaccepted argument is supplied, function merely returns status.

This function is not exported by default, but may be exported
with the C<:utf8> or C<:all> export tag.

=cut

my $unicode_module = eval { require Unicode::String };
my $UNICODE = 0;

sub use_mp3_utf8 {
	my($val) = @_;
	if ($val == 1) {
		$UNICODE = 1 if $unicode_module;
	} elsif ($val == 0) {
		$UNICODE = 0;
	}
	return $UNICODE;
}

=pod

=item use_winamp_genres()

Puts WinAmp genres into C<@mp3_genres> and C<%mp3_genres>
(adds 68 additional genres to the default list of 80).
This is a separate function because these are non-standard
genres, but they are included because they are widely used.

You can import the data structures with one of:

	use MP3::Info qw(:genres);
	use MP3::Info qw(:DEFAULT :genres);
	use MP3::Info qw(:all);

=cut

sub use_winamp_genres {
	%mp3_genres = %winamp_genres;
	@mp3_genres = @winamp_genres;
	return 1;
}

=pod

=item remove_mp3tag (FILE [, VERSION, BUFFER])

Can remove ID3v1 or ID3v2 tags.  VERSION should be C<1> for ID3v1,
C<2> for ID3v2, and C<ALL> for both.

For ID3v1, removes last 128 bytes from file if those last 128 bytes begin
with the text 'TAG'.  File will be 128 bytes shorter.

For ID3v2, removes ID3v2 tag.  Because an ID3v2 tag is at the
beginning of the file, we rewrite the file after removing the tag data.
The buffer for rewriting the file is 4MB.  BUFFER (in bytes) ca
change the buffer size.

Returns the number of bytes removed, or -1 if no tag removed,
or undef if there is an error.

=cut

sub remove_mp3tag {
	my($file, $version, $buf) = @_;
	my($fh, $return);

	$buf ||= 4096*1024;  # the bigger the faster
	$version ||= 1;

	if (not (defined $file && $file ne '')) {
		$@ = "No file specified";
		return undef;
	}

	if (not -s $file) {
		$@ = "File is empty";
		return undef;
	}

	if (ref $file) { # filehandle passed
		$fh = $file;
	} else {
		$fh = gensym;
		if (not open $fh, "+< $file\0") {
			$@ = "Can't open $file: $!";
			return undef;
		}
	}

	binmode $fh;

	if ($version eq 1 || $version eq 'ALL') {
		seek $fh, -128, 2;
		my $tell = tell $fh;
		if (<$fh> =~ /^TAG/) {
			truncate $fh, $tell or carp "Can't truncate '$file': $!";
			$return += 128;
		}
	}

	if ($version eq 2 || $version eq 'ALL') {
		my $h = _get_v2head($fh);
		if ($h) {
			local $\;
			seek $fh, 0, 2;
			my $eof = tell $fh;
			my $off = $h->{tag_size};

			while ($off < $eof) {
				seek $fh, $off, 0;
				read $fh, my($bytes), $buf;
				seek $fh, $off - $h->{tag_size}, 0;
				print $fh $bytes;
				$off += $buf;
			}

			truncate $fh, $eof - $h->{tag_size}
				or carp "Can't truncate '$file': $!";
			$return += $h->{tag_size};
		}
	}

	_close($file, $fh);

	return $return || -1;
}


=pod

=item set_mp3tag (FILE, TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE [, TRACKNUM])

=item set_mp3tag (FILE, $HASHREF)

Adds/changes tag information in an MP3 audio file.  Will clobber
any existing information in file.

Fields are TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE.  All fields have
a 30-byte limit, except for YEAR, which has a four-byte limit, and GENRE,
which is one byte in the file.  The GENRE passed in the function is a
case-insensitive text string representing a genre found in C<@mp3_genres>.

Will accept either a list of values, or a hashref of the type
returned by C<get_mp3tag>.

If TRACKNUM is present (for ID3v1.1), then the COMMENT field can only be
28 bytes.

ID3v2 support may come eventually.  Note that if you set a tag on a file
with ID3v2, the set tag will be for ID3v1[.1] only, and if you call
C<get_mp3_tag> on the file, it will show you the (unchanged) ID3v2 tags,
unless you specify ID3v1.

=cut

sub set_mp3tag {
	my($file, $title, $artist, $album, $year, $comment, $genre, $tracknum) = @_;
	my(%info, $oldfh, $ref, $fh);
	local %v1_tag_fields = %v1_tag_fields;

	# set each to '' if undef
	for ($title, $artist, $album, $year, $comment, $tracknum, $genre,
		(@info{@v1_tag_names}))
		{$_ = defined() ? $_ : ''}

	($ref) = (overload::StrVal($title) =~ /^(?:.*\=)?([^=]*)\((?:[^\(]*)\)$/)
		if ref $title;
	# populate data to hashref if hashref is not passed
	if (!$ref) {
		(@info{@v1_tag_names}) =
			($title, $artist, $album, $year, $comment, $tracknum, $genre);

	# put data from hashref into hashref if hashref is passed
	} elsif ($ref eq 'HASH') {
		%info = %$title;

	# return otherwise
	} else {
		carp(<<'EOT');
Usage: set_mp3tag (FILE, TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE [, TRACKNUM])
       set_mp3tag (FILE, $HASHREF)
EOT
		return undef;
	}

	if (not (defined $file && $file ne '')) {
		$@ = "No file specified";
		return undef;
	}

	if (not -s $file) {
		$@ = "File is empty";
		return undef;
	}

	# comment field length 28 if ID3v1.1
	$v1_tag_fields{COMMENT} = 28 if $info{TRACKNUM};


	# only if -w is on
	if ($^W) {
		# warn if fields too long
		foreach my $field (keys %v1_tag_fields) {
			$info{$field} = '' unless defined $info{$field};
			if (length($info{$field}) > $v1_tag_fields{$field}) {
				carp "Data too long for field $field: truncated to " .
					 "$v1_tag_fields{$field}";
			}
		}

		if ($info{GENRE}) {
			carp "Genre `$info{GENRE}' does not exist\n"
				unless exists $mp3_genres{$info{GENRE}};
		}
	}

	if ($info{TRACKNUM}) {
		$info{TRACKNUM} =~ s/^(\d+)\/(\d+)$/$1/;
		unless ($info{TRACKNUM} =~ /^\d+$/ &&
			$info{TRACKNUM} > 0 && $info{TRACKNUM} < 256) {
			carp "Tracknum `$info{TRACKNUM}' must be an integer " .
				"from 1 and 255\n" if $^W;
			$info{TRACKNUM} = '';
		}
	}

	if (ref $file) { # filehandle passed
		$fh = $file;
	} else {
		$fh = gensym;
		if (not open $fh, "+< $file\0") {
			$@ = "Can't open $file: $!";
			return undef;
		}
	}

	binmode $fh;
	$oldfh = select $fh;
	seek $fh, -128, 2;
	# go to end of file if no tag, beginning of file if tag
	seek $fh, (<$fh> =~ /^TAG/ ? -128 : 0), 2;

	# get genre value
	$info{GENRE} = $info{GENRE} && exists $mp3_genres{$info{GENRE}} ?
		$mp3_genres{$info{GENRE}} : 255;  # some default genre

	local $\;
	# print TAG to file
	if ($info{TRACKNUM}) {
		print pack "a3a30a30a30a4a28xCC", 'TAG', @info{@v1_tag_names};
	} else {
		print pack "a3a30a30a30a4a30C", 'TAG', @info{@v1_tag_names[0..4, 6]};
	}

	select $oldfh;

	_close($file, $fh);

	return 1;
}

=pod

=item get_mp3tag (FILE [, VERSION, RAW_V2])

Returns hash reference containing tag information in MP3 file.  The keys
returned are the same as those supplied for C<set_mp3tag>, except in the
case of RAW_V2 being set.

If VERSION is C<1>, the information is taken from the ID3v1 tag (if present).
If VERSION is C<2>, the information is taken from the ID3v2 tag (if present).
If VERSION is not supplied, or is false, the ID3v1 tag is read if present, and
then, if present, the ID3v2 tag information will override any existing ID3v1
tag info.

If RAW_V2 is C<1>, the raw ID3v2 tag data is returned, without any manipulation
of text encoding.  The key name is the same as the frame ID (ID to name mappings
are in the global %v2_tag_names).

If RAW_V2 is C<2>, the ID3v2 tag data is returned, manipulating for Unicode if
necessary, etc.  It also takes multiple values for a given key (such as comments)
and puts them in an arrayref.

If the ID3v2 version is older than ID3v2.2.0 or newer than ID3v2.4.0, it will
not be read.

Strings returned will be in Latin-1, unless UTF-8 is specified (L<use_mp3_utf8>),
(unless RAW_V2 is C<1>).

Also returns a TAGVERSION key, containing the ID3 version used for the returned
data (if TAGVERSION argument is C<0>, may contain two versions).

=cut

sub get_mp3tag {
	my($file, $ver, $raw_v2) = @_;
	my($tag, $v1, $v2, $v2h, %info, @array, $fh);
	$raw_v2 ||= 0;
	$ver = !$ver ? 0 : ($ver == 2 || $ver == 1) ? $ver : 0;

	if (not (defined $file && $file ne '')) {
		$@ = "No file specified";
		return undef;
	}

	if (not -s $file) {
		$@ = "File is empty";
		return undef;
	}

	if (ref $file) { # filehandle passed
		$fh = $file;
	} else {
		$fh = gensym;
		if (not open $fh, "< $file\0") {
			$@ = "Can't open $file: $!";
			return undef;
		}
	}

	binmode $fh;

	if ($ver < 2) {
		seek $fh, -128, 2;
		while(defined(my $line = <$fh>)) { $tag .= $line }

		if ($tag =~ /^TAG/) {
			$v1 = 1;
			if (substr($tag, -3, 2) =~ /\000[^\000]/) {
				(undef, @info{@v1_tag_names}) =
					(unpack('a3a30a30a30a4a28', $tag),
					ord(substr($tag, -2, 1)),
					$mp3_genres[ord(substr $tag, -1)]);
				$info{TAGVERSION} = 'ID3v1.1';
			} else {
				(undef, @info{@v1_tag_names[0..4, 6]}) =
					(unpack('a3a30a30a30a4a30', $tag),
					$mp3_genres[ord(substr $tag, -1)]);
				$info{TAGVERSION} = 'ID3v1';
			}
			if ($UNICODE) {
				for my $key (keys %info) {
					next unless $info{$key};
					my $u = Unicode::String::latin1($info{$key});
					$info{$key} = $u->utf8;
				}
			}
		} elsif ($ver == 1) {
			_close($file, $fh);
			$@ = "No ID3v1 tag found";
			return undef;
		}
	}

	($v2, $v2h) = _get_v2tag($fh);

	unless ($v1 || $v2) {
		_close($file, $fh);
		$@ = "No ID3 tag found";
		return undef;
	}

	if (($ver == 0 || $ver == 2) && $v2) {
		if ($raw_v2 == 1 && $ver == 2) {
			%info = %$v2;
			$info{TAGVERSION} = $v2h->{version};
		} else {
			my $hash = $raw_v2 == 2 ? { map { ($_, $_) } keys %v2_tag_names } : \%v2_to_v1_names;
			for my $id (keys %$hash) {
				if (exists $v2->{$id}) {
					if ($id =~ /^TCON?$/ && $v2->{$id} =~ /^.?\((\d+)\)/) {
						$info{$hash->{$id}} = $mp3_genres[$1];
					} else {
						my $data1 = $v2->{$id};

						# this is tricky ... if this is an arrayref,
						# we want to only return one, so we pick the
						# first one.  but if it is a comment, we pick
						# the first one where the first charcter after
						# the language is NULL and not an additional
						# sub-comment, because that is most likely to be
						# the user-supplied comment
						if (ref $data1 && !$raw_v2) {
							if ($id =~ /^COMM?$/) {
								my($newdata) = grep /^(....\000)/, @{$data1};
								$data1 = $newdata || $data1->[0];
							} else {
								$data1 = $data1->[0];
							}
						}

						$data1 = [ $data1 ] if ! ref $data1;

						for my $data (@$data1) {
							$data =~ s/^(.)//; # strip first char (text encoding)
							my $encoding = $1;
							my $desc;
							if ($id =~ /^COM[M ]?$/) {
								$data =~ s/^(?:...)//;		# strip language
								$data =~ s/^(.*?)\000+//;	# strip up to first NULL(s),
												# for sub-comment
								$desc = $1;
							}

							if ($UNICODE) {
								if ($encoding eq "\001" || $encoding eq "\002") {  # UTF-16, UTF-16BE
									my $u = Unicode::String::utf16($data);
									$data = $u->utf8;
									$data =~ s/^\xEF\xBB\xBF//;	# strip BOM
								} elsif ($encoding eq "\000") {
									my $u = Unicode::String::latin1($data);
									$data = $u->utf8;
								}
							}

							if ($raw_v2 == 2 && $desc) {
								$data = { $desc => $data };
							}

							if ($raw_v2 == 2 && exists $info{$hash->{$id}}) {
								if (ref $info{$hash->{$id}} eq 'ARRAY') {
									push @{$info{$hash->{$id}}}, $data;
								} else {
									$info{$hash->{$id}} = [ $info{$hash->{$id}}, $data ];
								}
							} else {
								$info{$hash->{$id}} = $data;
							}
						}
					}
				}
			}
			if ($ver == 0 && $info{TAGVERSION}) {
				$info{TAGVERSION} .= ' / ' . $v2h->{version};
			} else {
				$info{TAGVERSION} = $v2h->{version};
			}
		}
	}

	unless ($raw_v2 && $ver == 2) {
		foreach my $key (keys %info) {
			if (defined $info{$key}) {
				$info{$key} =~ s/\000+.*//g;
				$info{$key} =~ s/\s+$//;
			}
		}

		for (@v1_tag_names) {
			$info{$_} = '' unless defined $info{$_};
		}
	}

	if (keys %info && exists $info{GENRE} && ! defined $info{GENRE}) {
		$info{GENRE} = '';
	}

	_close($file, $fh);

	return keys %info ? {%info} : undef;
}

sub _get_v2tag {
	my($fh) = @_;
	my($off, $myseek, $myseek_22, $myseek_23, $v2, $h, $hlen, $num);
	$h = {};

	$v2 = _get_v2head($fh) or return;
	if ($v2->{major_version} < 2) {
		carp "This is $v2->{version}; " .
		     "ID3v2 versions older than ID3v2.2.0 not supported\n"
		     if $^W;
		return;
	}

	if ($v2->{major_version} == 2) {
		$hlen = 6;
		$num = 3;
	} else {
		$hlen = 10;
		$num = 4;
	}

	$myseek = sub {
		seek $fh, $off, 0;
		read $fh, my($bytes), $hlen;
		return unless $bytes =~ /^([A-Z0-9]{$num})/
			|| ($num == 4 && $bytes =~ /^(COM )/);  # stupid iTunes
		my($id, $size) = ($1, $hlen);
		my @bytes = reverse unpack "C$num", substr($bytes, $num, $num);
		for my $i (0 .. ($num - 1)) {
			$size += $bytes[$i] * 256 ** $i;
		}
		return($id, $size);
	};

	$off = $v2->{ext_header_size} + 10;

	while ($off < $v2->{tag_size}) {
		my($id, $size) = &$myseek or last;
		seek $fh, $off + $hlen, 0;
		read $fh, my($bytes), $size - $hlen;
		if (exists $h->{$id}) {
			if (ref $h->{$id} eq 'ARRAY') {
				push @{$h->{$id}}, $bytes;
			} else {
				$h->{$id} = [$h->{$id}, $bytes];
			}
		} else {
			$h->{$id} = $bytes;
		}
		$off += $size;
	}

	return($h, $v2);
}


=pod

=item get_mp3info (FILE)

Returns hash reference containing file information for MP3 file.
This data cannot be changed.  Returned data:

	VERSION		MPEG audio version (1, 2, 2.5)
	LAYER		MPEG layer description (1, 2, 3)
	STEREO		boolean for audio is in stereo

	VBR		boolean for variable bitrate
	BITRATE		bitrate in kbps (average for VBR files)
	FREQUENCY	frequency in kHz
	SIZE		bytes in audio stream

	SECS		total seconds
	MM		minutes
	SS		leftover seconds
	MS		leftover milliseconds
	TIME		time in MM:SS

	COPYRIGHT	boolean for audio is copyrighted
	PADDING		boolean for MP3 frames are padded
	MODE		channel mode (0 = stereo, 1 = joint stereo,
			2 = dual channel, 3 = single channel)
	FRAMES		approximate number of frames
	FRAME_LENGTH	approximate length of a frame
	VBR_SCALE	VBR scale from VBR header

On error, returns nothing and sets C<$@>.

=cut

sub get_mp3info {
	my($file) = @_;
	my($off, $myseek, $byte, $eof, $h, $tot, $fh);

	if (not (defined $file && $file ne '')) {
		$@ = "No file specified";
		return undef;
	}

	if (not -s $file) {
		$@ = "File is empty";
		return undef;
	}

	if (ref $file) { # filehandle passed
		$fh = $file;
	} else {
		$fh = gensym;
		if (not open $fh, "< $file\0") {
			$@ = "Can't open $file: $!";
			return undef;
		}
	}

	$off = 0;
	$tot = 4096;

	$myseek = sub {
		seek $fh, $off, 0;
		read $fh, $byte, 4;
	};

	binmode $fh;
	&$myseek;

	if ($off == 0) {
		if (my $id3v2 = _get_v2head($fh)) {
			$tot += $off += $id3v2->{tag_size};
			&$myseek;
		}
	}

	$h = _get_head($byte);
	until (_is_mp3($h)) {
		$off++;
		&$myseek;
		$h = _get_head($byte);
		if ($off > $tot && !$try_harder) {
			_close($file, $fh);
			$@ = "Couldn't find MP3 header (perhaps set " .
			     '$MP3::Info::try_harder and retry)';
			return undef;
		}
	}

	my $vbr = _get_vbr($fh, $h, \$off);

	seek $fh, 0, 2;
	$eof = tell $fh;
	seek $fh, -128, 2;
	$off += 128 if <$fh> =~ /^TAG/ ? 1 : 0;

	_close($file, $fh);

	$h->{size} = $eof - $off;

	return _get_info($h, $vbr);
}

sub _get_info {
	my($h, $vbr) = @_;
	my $i;

	$i->{VERSION}	= $h->{IDR} == 2 ? 2 : $h->{IDR} == 3 ? 1 :
				$h->{IDR} == 0 ? 2.5 : 0;
	$i->{LAYER}	= 4 - $h->{layer};
	$i->{VBR}	= defined $vbr ? 1 : 0;

	$i->{COPYRIGHT}	= $h->{copyright} ? 1 : 0;
	$i->{PADDING}	= $h->{padding_bit} ? 1 : 0;
	$i->{STEREO}	= $h->{mode} == 3 ? 0 : 1;
	$i->{MODE}	= $h->{mode};

	$i->{SIZE}	= $vbr && $vbr->{bytes} ? $vbr->{bytes} : $h->{size};

	my $mfs		= $h->{fs} / ($h->{ID} ? 144000 : 72000);
	$i->{FRAMES}	= int($vbr && $vbr->{frames}
				? $vbr->{frames}
				: $i->{SIZE} / $h->{bitrate} / $mfs
			  );

	if ($vbr) {
		$i->{VBR_SCALE}	= $vbr->{scale} if $vbr->{scale};
		$h->{bitrate}	= $i->{SIZE} / $i->{FRAMES} * $mfs;
		if (not $h->{bitrate}) {
			$@ = "Couldn't determine VBR bitrate";
			return undef;
		}
	}

	$h->{'length'}	= ($i->{SIZE} * 8) / $h->{bitrate} / 10;
	$i->{SECS}	= $h->{'length'} / 100;
	$i->{MM}	= int $i->{SECS} / 60;
	$i->{SS}	= int $i->{SECS} % 60;
	$i->{MS}	= (($i->{SECS} - ($i->{MM} * 60) - $i->{SS}) * 1000);
#	$i->{LF}	= ($i->{MS} / 1000) * ($i->{FRAMES} / $i->{SECS});
#	int($i->{MS} / 100 * 75);  # is this right?
	$i->{TIME}	= sprintf "%.2d:%.2d", @{$i}{'MM', 'SS'};

	$i->{BITRATE}		= int $h->{bitrate};
	# should we just return if ! FRAMES?
	$i->{FRAME_LENGTH}	= int($h->{size} / $i->{FRAMES}) if $i->{FRAMES};
	$i->{FREQUENCY}		= $frequency_tbl[3 * $h->{IDR} + $h->{sampling_freq}];

	return $i;
}

sub _get_head {
	my($byte) = @_;
	my($bytes, $h);

	$bytes = _unpack_head($byte);
	@$h{qw(IDR ID layer protection_bit
		bitrate_index sampling_freq padding_bit private_bit
		mode mode_extension copyright original
		emphasis version_index bytes)} = (
		($bytes>>19)&3, ($bytes>>19)&1, ($bytes>>17)&3, ($bytes>>16)&1,
		($bytes>>12)&15, ($bytes>>10)&3, ($bytes>>9)&1, ($bytes>>8)&1,
		($bytes>>6)&3, ($bytes>>4)&3, ($bytes>>3)&1, ($bytes>>2)&1,
		$bytes&3, ($bytes>>19)&3, $bytes
	);

	$h->{bitrate} = $t_bitrate[$h->{ID}][3 - $h->{layer}][$h->{bitrate_index}];
	$h->{fs} = $t_sampling_freq[$h->{IDR}][$h->{sampling_freq}];

	return $h;
}

sub _is_mp3 {
	my $h = $_[0] or return undef;
	return ! (	# all below must be false
		 $h->{bitrate_index} == 0
			||
		 $h->{version_index} == 1
			||
		($h->{bytes} & 0xFFE00000) != 0xFFE00000
			||
		!$h->{fs}
			||
		!$h->{bitrate}
			||
		 $h->{bitrate_index} == 15
			||
		!$h->{layer}
			||
		 $h->{sampling_freq} == 3
			||
		 $h->{emphasis} == 2
			||
		!$h->{bitrate_index}
			||
		($h->{bytes} & 0xFFFF0000) == 0xFFFE0000
			||
		($h->{ID} == 1 && $h->{layer} == 3 && $h->{protection_bit} == 1)
			||
		($h->{mode_extension} != 0 && $h->{mode} != 1)
	);
}

sub _get_vbr {
	my($fh, $h, $roff) = @_;
	my($off, $bytes, @bytes, $myseek, %vbr);

	$off = $$roff;
	@_ = ();	# closure confused if we don't do this

	$myseek = sub {
		my $n = $_[0] || 4;
		seek $fh, $off, 0;
		read $fh, $bytes, $n;
		$off += $n;
	};

	$off += 4;

	if ($h->{ID}) {	# MPEG1
		$off += $h->{mode} == 3 ? 17 : 32;
	} else {	# MPEG2
		$off += $h->{mode} == 3 ? 9 : 17;
	}

	&$myseek;
	return unless $bytes eq 'Xing';

	&$myseek;
	$vbr{flags} = _unpack_head($bytes);

	if ($vbr{flags} & 1) {
		&$myseek;
		$vbr{frames} = _unpack_head($bytes);
	}

	if ($vbr{flags} & 2) {
		&$myseek;
		$vbr{bytes} = _unpack_head($bytes);
	}

	if ($vbr{flags} & 4) {
		$myseek->(100);
# Not used right now ...
#		$vbr{toc} = _unpack_head($bytes);
	}

	if ($vbr{flags} & 8) { # (quality ind., 0=best 100=worst)
		&$myseek;
		$vbr{scale} = _unpack_head($bytes);
	} else {
		$vbr{scale} = -1;
	}

	$$roff = $off;
	return \%vbr;
}

sub _get_v2head {
	my $fh = $_[0] or return;
	my($h, $bytes, @bytes);

	# check first three bytes for 'ID3'
	seek $fh, 0, 0;
	read $fh, $bytes, 3;
	return unless $bytes eq 'ID3';

	# get version
	read $fh, $bytes, 2;
	$h->{version} = sprintf "ID3v2.%d.%d",
		@$h{qw[major_version minor_version]} =
			unpack 'c2', $bytes;

	# get flags
	read $fh, $bytes, 1;
	if ($h->{major_version} == 2) {
		@$h{qw[unsync compression]} =
			(unpack 'b8', $bytes)[7, 6];
		$h->{ext_header} = 0;
		$h->{experimental} = 0;
	} else {
		@$h{qw[unsync ext_header experimental]} =
			(unpack 'b8', $bytes)[7, 6, 5];
	}

	# get ID3v2 tag length from bytes 7-10
	$h->{tag_size} = 10;	# include ID3v2 header size
	read $fh, $bytes, 4;
	@bytes = reverse unpack 'C4', $bytes;
	foreach my $i (0 .. 3) {
		# whoaaaaaa nellllllyyyyyy!
		$h->{tag_size} += $bytes[$i] * 128 ** $i;
	}

	# get extended header size
	$h->{ext_header_size} = 0;
	if ($h->{ext_header}) {
		$h->{ext_header_size} += 10;
		read $fh, $bytes, 4;
		@bytes = reverse unpack 'C4', $bytes;
		for my $i (0..3) {
			$h->{ext_header_size} += $bytes[$i] * 256 ** $i;
		}
	}

	return $h;
}

sub _unpack_head {
	unpack('l', pack('L', unpack('N', $_[0])));
}

sub _close {
	my($file, $fh) = @_;
	unless (ref $file) { # filehandle not passed
		close $fh or carp "Problem closing '$file': $!";
	}
}

BEGIN {
	@mp3_genres = (
		'Blues',
		'Classic Rock',
		'Country',
		'Dance',
		'Disco',
		'Funk',
		'Grunge',
		'Hip-Hop',
		'Jazz',
		'Metal',
		'New Age',
		'Oldies',
		'Other',
		'Pop',
		'R&B',
		'Rap',
		'Reggae',
		'Rock',
		'Techno',
		'Industrial',
		'Alternative',
		'Ska',
		'Death Metal',
		'Pranks',
		'Soundtrack',
		'Euro-Techno',
		'Ambient',
		'Trip-Hop',
		'Vocal',
		'Jazz+Funk',
		'Fusion',
		'Trance',
		'Classical',
		'Instrumental',
		'Acid',
		'House',
		'Game',
		'Sound Clip',
		'Gospel',
		'Noise',
		'Alt. Rock',
		'Bass',
		'Soul',
		'Punk',
		'Space',
		'Meditative',
		'Instrumental Pop',
		'Instrumental Rock',
		'Ethnic',
		'Gothic',
		'Darkwave',
		'Techno-Industrial',
		'Electronic',
		'Pop-Folk',
		'Eurodance',
		'Dream',
		'Southern Rock',
		'Comedy',
		'Cult',
		'Gangsta',
		'Top 40',
		'Christian Rap',
		'Pop/Funk',
		'Jungle',
		'Native American',
		'Cabaret',
		'New Wave',
		'Psychadelic',
		'Rave',
		'Showtunes',
		'Trailer',
		'Lo-Fi',
		'Tribal',
		'Acid Punk',
		'Acid Jazz',
		'Polka',
		'Retro',
		'Musical',
		'Rock & Roll',
		'Hard Rock',
		'Folk',
		'Folk-Rock',
		'National Folk',
		'Swing',
		'Fast Fusion',
		'Bebob',
		'Latin',
		'Revival',
		'Celtic',
		'Bluegrass',
		'Avantgarde',
		'Gothic Rock',
		'Progressive Rock',
		'Psychedelic Rock',
		'Symphonic Rock',
		'Slow Rock',
		'Big Band',
		'Chorus',
		'Easy Listening',
		'Acoustic',
		'Humour',
		'Speech',
		'Chanson',
		'Opera',
		'Chamber Music',
		'Sonata',
		'Symphony',
		'Booty Bass',
		'Primus',
		'Porn Groove',
		'Satire',
		'Slow Jam',
		'Club',
		'Tango',
		'Samba',
		'Folklore',
		'Ballad',
		'Power Ballad',
		'Rhythmic Soul',
		'Freestyle',
		'Duet',
		'Punk Rock',
		'Drum Solo',
		'Acapella',
		'Euro-House',
		'Dance Hall',
		'Goa',
		'Drum & Bass',
		'Club-House',
		'Hardcore',
		'Terror',
		'Indie',
		'BritPop',
		'Negerpunk',
		'Polsk Punk',
		'Beat',
		'Christian Gangsta Rap',
		'Heavy Metal',
		'Black Metal',
		'Crossover',
		'Contemporary Christian',
		'Christian Rock',
		'Merengue',
		'Salsa',
		'Thrash Metal',
		'Anime',
		'JPop',
		'Synthpop',
		'MPB',
		'Bossa Nova',
	);

	@winamp_genres = @mp3_genres;

	@t_bitrate = ([
		[0, 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256],
		[0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160],
		[0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160]
	],[
		[0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448],
		[0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384],
		[0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320]
	]);

	@t_sampling_freq = (
		[11025, 12000,  8000],
		[undef, undef, undef],	# reserved
		[22050, 24000, 16000],
		[44100, 48000, 32000]
	);

	@frequency_tbl = map { $_ ? eval "${_}e-3" : 0 }
		map { @$_ } @t_sampling_freq;

	@mp3_info_fields = qw(
		VERSION
		LAYER
		STEREO
		VBR
		BITRATE
		FREQUENCY
		SIZE
		SECS
		MM
		SS
		MS
		TIME
		COPYRIGHT
		PADDING
		MODE
		FRAMES
		FRAME_LENGTH
		VBR_SCALE
	);

	%v1_tag_fields =
		(TITLE => 30, ARTIST => 30, ALBUM => 30, COMMENT => 30, YEAR => 4);

	@v1_tag_names = qw(TITLE ARTIST ALBUM YEAR COMMENT TRACKNUM GENRE);

	%v2_to_v1_names = (
		# v2.2 tags
		'TT2' => 'TITLE',
		'TP1' => 'ARTIST',
		'TAL' => 'ALBUM',
		'TYE' => 'YEAR',
		'COM' => 'COMMENT',
		'TRK' => 'TRACKNUM',
		'TCO' => 'GENRE', # not clean mapping, but ...
		# v2.3 tags
		'TIT2' => 'TITLE',
		'TPE1' => 'ARTIST',
		'TALB' => 'ALBUM',
		'TYER' => 'YEAR',
		'COMM' => 'COMMENT',
		'TRCK' => 'TRACKNUM',
		'TCON' => 'GENRE',
	);

	%v2_tag_names = (
		# v2.2 tags
		'BUF' => 'Recommended buffer size',
		'CNT' => 'Play counter',
		'COM' => 'Comments',
		'CRA' => 'Audio encryption',
		'CRM' => 'Encrypted meta frame',
		'ETC' => 'Event timing codes',
		'EQU' => 'Equalization',
		'GEO' => 'General encapsulated object',
		'IPL' => 'Involved people list',
		'LNK' => 'Linked information',
		'MCI' => 'Music CD Identifier',
		'MLL' => 'MPEG location lookup table',
		'PIC' => 'Attached picture',
		'POP' => 'Popularimeter',
		'REV' => 'Reverb',
		'RVA' => 'Relative volume adjustment',
		'SLT' => 'Synchronized lyric/text',
		'STC' => 'Synced tempo codes',
		'TAL' => 'Album/Movie/Show title',
		'TBP' => 'BPM (Beats Per Minute)',
		'TCM' => 'Composer',
		'TCO' => 'Content type',
		'TCR' => 'Copyright message',
		'TDA' => 'Date',
		'TDY' => 'Playlist delay',
		'TEN' => 'Encoded by',
		'TFT' => 'File type',
		'TIM' => 'Time',
		'TKE' => 'Initial key',
		'TLA' => 'Language(s)',
		'TLE' => 'Length',
		'TMT' => 'Media type',
		'TOA' => 'Original artist(s)/performer(s)',
		'TOF' => 'Original filename',
		'TOL' => 'Original Lyricist(s)/text writer(s)',
		'TOR' => 'Original release year',
		'TOT' => 'Original album/Movie/Show title',
		'TP1' => 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group',
		'TP2' => 'Band/Orchestra/Accompaniment',
		'TP3' => 'Conductor/Performer refinement',
		'TP4' => 'Interpreted, remixed, or otherwise modified by',
		'TPA' => 'Part of a set',
		'TPB' => 'Publisher',
		'TRC' => 'ISRC (International Standard Recording Code)',
		'TRD' => 'Recording dates',
		'TRK' => 'Track number/Position in set',
		'TSI' => 'Size',
		'TSS' => 'Software/hardware and settings used for encoding',
		'TT1' => 'Content group description',
		'TT2' => 'Title/Songname/Content description',
		'TT3' => 'Subtitle/Description refinement',
		'TXT' => 'Lyricist/text writer',
		'TXX' => 'User defined text information frame',
		'TYE' => 'Year',
		'UFI' => 'Unique file identifier',
		'ULT' => 'Unsychronized lyric/text transcription',
		'WAF' => 'Official audio file webpage',
		'WAR' => 'Official artist/performer webpage',
		'WAS' => 'Official audio source webpage',
		'WCM' => 'Commercial information',
		'WCP' => 'Copyright/Legal information',
		'WPB' => 'Publishers official webpage',
		'WXX' => 'User defined URL link frame',

		# v2.3 tags
		'AENC' => 'Audio encryption',
		'APIC' => 'Attached picture',
		'COMM' => 'Comments',
		'COMR' => 'Commercial frame',
		'ENCR' => 'Encryption method registration',
		'EQUA' => 'Equalization',
		'ETCO' => 'Event timing codes',
		'GEOB' => 'General encapsulated object',
		'GRID' => 'Group identification registration',
		'IPLS' => 'Involved people list',
		'LINK' => 'Linked information',
		'MCDI' => 'Music CD identifier',
		'MLLT' => 'MPEG location lookup table',
		'OWNE' => 'Ownership frame',
		'PCNT' => 'Play counter',
		'POPM' => 'Popularimeter',
		'POSS' => 'Position synchronisation frame',
		'PRIV' => 'Private frame',
		'RBUF' => 'Recommended buffer size',
		'RVAD' => 'Relative volume adjustment',
		'RVRB' => 'Reverb',
		'SYLT' => 'Synchronized lyric/text',
		'SYTC' => 'Synchronized tempo codes',
		'TALB' => 'Album/Movie/Show title',
		'TBPM' => 'BPM (beats per minute)',
		'TCOM' => 'Composer',
		'TCON' => 'Content type',
		'TCOP' => 'Copyright message',
		'TDAT' => 'Date',
		'TDLY' => 'Playlist delay',
		'TENC' => 'Encoded by',
		'TEXT' => 'Lyricist/Text writer',
		'TFLT' => 'File type',
		'TIME' => 'Time',
		'TIT1' => 'Content group description',
		'TIT2' => 'Title/songname/content description',
		'TIT3' => 'Subtitle/Description refinement',
		'TKEY' => 'Initial key',
		'TLAN' => 'Language(s)',
		'TLEN' => 'Length',
		'TMED' => 'Media type',
		'TOAL' => 'Original album/movie/show title',
		'TOFN' => 'Original filename',
		'TOLY' => 'Original lyricist(s)/text writer(s)',
		'TOPE' => 'Original artist(s)/performer(s)',
		'TORY' => 'Original release year',
		'TOWN' => 'File owner/licensee',
		'TPE1' => 'Lead performer(s)/Soloist(s)',
		'TPE2' => 'Band/orchestra/accompaniment',
		'TPE3' => 'Conductor/performer refinement',
		'TPE4' => 'Interpreted, remixed, or otherwise modified by',
		'TPOS' => 'Part of a set',
		'TPUB' => 'Publisher',
		'TRCK' => 'Track number/Position in set',
		'TRDA' => 'Recording dates',
		'TRSN' => 'Internet radio station name',
		'TRSO' => 'Internet radio station owner',
		'TSIZ' => 'Size',
		'TSRC' => 'ISRC (international standard recording code)',
		'TSSE' => 'Software/Hardware and settings used for encoding',
		'TXXX' => 'User defined text information frame',
		'TYER' => 'Year',
		'UFID' => 'Unique file identifier',
		'USER' => 'Terms of use',
		'USLT' => 'Unsychronized lyric/text transcription',
		'WCOM' => 'Commercial information',
		'WCOP' => 'Copyright/Legal information',
		'WOAF' => 'Official audio file webpage',
		'WOAR' => 'Official artist/performer webpage',
		'WOAS' => 'Official audio source webpage',
		'WORS' => 'Official internet radio station homepage',
		'WPAY' => 'Payment',
		'WPUB' => 'Publishers official webpage',
		'WXXX' => 'User defined URL link frame',

		# v2.4 additional tags
		# note that we don't restrict tags from 2.3 or 2.4,
		'ASPI' => 'Audio seek point index',
		'EQU2' => 'Equalisation (2)',
		'RVA2' => 'Relative volume adjustment (2)',
		'SEEK' => 'Seek frame',
		'SIGN' => 'Signature frame',
		'TDEN' => 'Encoding time',
		'TDOR' => 'Original release time',
		'TDRC' => 'Recording time',
		'TDRL' => 'Release time',
		'TDTG' => 'Tagging time',
		'TIPL' => 'Involved people list',
		'TMCL' => 'Musician credits list',
		'TMOO' => 'Mood',
		'TPRO' => 'Produced notice',
		'TSOA' => 'Album sort order',
		'TSOP' => 'Performer sort order',
		'TSOT' => 'Title sort order',
		'TSST' => 'Set subtitle',

		# grrrrrrr
		'COM ' => 'Broken iTunes comments',
	);
}

1;

__END__


Copyright (c) 1998-2003 Chris Nandor.  All rights reserved.  This program is
free software; you can redistribute it and/or modify it under the terms
of the Artistic License, distributed with Perl.





