1#!/usr/local/bin/perl
2require 5.008;                 # require minimum Perl version 5.8 - support for Unicode.
3use Gtk2 -init;                # load the Gtk-Perl module - see <man Gtk2>
4use Glib qw(filename_from_unicode filename_to_unicode filename_display_name);
5use Gtk2::Pango;               # to change fonts
6use strict;                    # a good idea for all non-trivial Perl programs
7use warnings;                  # force print warnings
8use File::Find;                # similar to the Unix find command, see <man File::Find>
9
10use Encode qw(encode decode is_utf8 _utf8_on _utf8_off); # see <man Encode>.
11use Unicode::Normalize qw(NFKD); # combining accent
12
13# use Fcntl;                   # for gnormalize::cdplay
14
15# See <man waitpid>, perlipc and Programming Perl, 3a Ed., Chap.16
16# waitpid Child_PID,FLAGS : "Waits for a particular child process to terminate and returns
17#                            the pid of the deceased process, or "-1" if there is no such child process."
18# waitpid(-1, WNOHANG) < 0 ; -1 : meaning wait for any child process. WNOHANG : return immediately if no child has exited.
19use POSIX  qw(:sys_wait_h fmod floor ceil);    # ':sys_wait_h' is used on sub 'change_count'
20
21# set_locale Gtk2;             # internationalize
22
23# use POSIX qw(locale_h);      # internationalize: see <man setlocale> POSIX, perlunicode and perllocale
24# Usage: POSIX::setlocale(category, locale = 0)
25# setlocale( LC_CTYPE, 'C');   # for regular expression matching, character classification,  conversion,
26                               # case-sensitive  comparison,  and  wide character functions
27# setlocale( LC_NUMERIC, 'C'); # for number formatting (such as the decimal point and  the  thousands separator).
28# setlocale( LC_ALL, 'iso-8859-1');
29
30$SIG{CHLD} = "IGNORE";         # kill zombies ; see man perlfunc and perlfork
31
32$|++;                          # Disable buffering by setting the perl-filehandle variable $| to a true value
33
34my $false = 0; my $true = 1;   # convenience variables for true and false
35my $id3tag_character = 'iso-8859-1'; # Latin1 for id3tag mp3 files.
36
37# use open IO => ":encoding(iso-8859-1)"; # set PerlIO layers for Input and Output. See 'man PerlIO'
38# BEGIN { $ENV{LC_ALL} = $ENV{LANG} = 'pt_BR' } # man perluniintro
39# use open OUT => ':locale';
40
41#------------------------- perl module: CDDB_get -----------------------------#
42# To get informations from audio cds - See <man CDDB_get>
43sub modules_cddb_get {
44   if (eval "use CDDB_get qw(get_cddb get_discids); 1;"){ return $true;}
45   else { warn  "\n\n  Not found CDDB_get perl module in your system.\n  gnormalize will uses internal CDDB_get (version 2.27).\n"; }
46   return $false;
47}
48my $use_external_cddbget = modules_cddb_get();
49
50#------------------------- perl module: MP3::Info ----------------------------#
51sub modules_mp3info { # Suggestions from Perl Cookbook, 2nd Edition, chapter 12.2 .
52   if (eval "use MP3::Info qw(:all); 1;"){ return $true;}
53   else { warn  "\n\n  Not found MP3::Info perl module in your system.\n  gnormalize will uses internal MP3::Info (version 1.20).\n"; }
54   return $false;
55}
56my $use_external_MP3Info = modules_mp3info();
57use_winamp_genres() if $use_external_MP3Info;
58
59#------------------------- perl module: Audio::CD ----------------------------#
60# Imports "Audio::CD" into current package if it exist
61my $use_audiocd = $false;
62sub modules_audio_cd { # suggestions from Perl Cookbook.
63   if (eval "use Audio::CD; 1;"){ $use_audiocd = $true;}
64   # else{ warn "Could not use Audio::CD \n $@";   }
65   return $use_audiocd;
66}
67modules_audio_cd();
68
69sub audio_cd_have_current_track { # return true if Audio::CD have current_track and $frames
70   my $audiodevice = shift;
71   if ( $use_audiocd eq $false ){ return $false; }
72
73   # umount the audio cd drive if possible. This is need to play EXTRA CD.
74
75   exec_cmd_system2('umount', "$audiodevice");
76
77   my $audiocd = Audio::CD->init("$audiodevice");
78   my $info = $audiocd->stat;
79
80   # See eval in perlfunc.
81   eval { $info->current_track; };
82   if ($@) {
83      warn "\n   Can't find the function \"current_track\".".
84           "\n   You must install the perl module \"Audio-CD-0.04-changed.tar.gz\".\n";
85      return $false;
86   }
87   my ($minutes, $seconds, $frames) = $info->time;
88   if (not defined($frames)){
89      warn "\n   Can't find the function \"my (\$minutes, \$seconds, \$frames) = \$info->time\".".
90           "\n   You must install the perl module \"Audio-CD-0.04-changed.tar.gz\".\n";
91      return $false;
92   }
93   return $true;
94}
95
96sub check_minimum_version {
97   my $gtk2_version = Gtk2->CHECK_VERSION (2, 6, 0); # boolean
98   my $gtk2_Perl_version = $Gtk2::VERSION;
99   if ( $gtk2_Perl_version < 1.100 or not $gtk2_version ) {
100      print "\n\$gtk2_Perl_version = $gtk2_Perl_version \n";
101      print "\nYou need a gtk2_Perl_version more recent!\n";
102      return $true;
103   }
104}
105check_minimum_version();
106
107#-----------------------------------------------------------------------------#
108
109# gtk+2.0 - 2.4.9 -9mdk  and  perl-Glib-1.080
110# examples: perl /usr/share/doc/perl-Gtk2-doc/gtk-demo/main.pl &
111# See:  man perl, perlfunc, perlintro, Gtk2, Gtk2::Window, Gtk2::index, ...
112
113# This program is free software; you can redistribute it and/or modify
114# it under the terms of the GNU General Public License as published by
115# the Free Software Foundation; either version 2, or (at your option)
116# any later version.
117
118# "Free Software, since you might like to learn a bit in the
119# process by looking at the code (or teach the author of the program how
120# to write better programs...)."
121
122# Copyright 2005/2008 - Claudio Fernandes de Souza Rodrigues
123# email: claudiofsr@yahoo.com
124
125# DISCLAIMER
126# The author assumes no responsibility for correct function, appropriate use, or misuse
127# of this program, and any damage its function, use or misuse may cause.
128
129# Pensamento filos�fico:
130# "A calcinha pode n�o ser a melhor coisa do mundo, mas est� bem perto!"
131
132##########------------Global-Variables------------##########
133###------------------------------------------------------###
134
135my $VERSION = "0.63";
136my $AUTHOR = "Claudio Fernandes de Souza Rodrigues";
137my $EMAIL = "claudiofsr\@yahoo.com";
138my $DATE = "Jul 10 2008";
139my $HOMEPAGE = "http://gnormalize.sourceforge.net";
140
141#----------------------------------------------------------#
142## ------- default path used by the cdrom drive --------- ##
143my $audiodevice_path ="/dev/cdrom";
144#----------------------------------------------------------#
145#----------------------------------------------------------#
146my $home = filename_to_unicode "$ENV{HOME}"; my $os;
147#my $datey = system("date +%Y");
148my $window; my $window_width = 860; my $window_height = 580;
149my $tooltips = Gtk2::Tooltips->new; #$tooltips->set_delay( 1000 ); # wait for 1s
150my $check_button_tooltips; my $show_all_tooltips = $true;
151my $notebook; my $notebook2; my $button; my $spinner;my $spinner_q; my $spinner_bitrate_mp3;
152my $spinner_bitrate_ogg; my $spinner_bitrate_mp4; my $spinner_Vmp4;
153my $vbox_up; my $vbox1; my $vbox2; my $button_save_tag;
154my $frame1; my $frame_prog_bar; my $have_current_track = $false;
155my $table1; my $table_down;
156my $norm_type = "Track"; my $num_d;
157my $file_dialog; my $button_clear;
158my $label_type; my $label_VB; my $entry_f; my $entry_dir_input;
159my $spinner_V; my $spinner_Vogg; my $spinner_Vmpc;
160my $label_mp3b; my $combo; my $show_tux_animation = $true; my $check_button_animation;
161my $label_Min; my $ComboBox_Min; my $vb_Min=64;
162my $label_Max; my $ComboBox_Max; my $vb_Max=320;
163my $ComboBox_rip; my $ComboBox_char;
164my $ComboBox_encode_type; my $ComboBox_const_bitrate;
165my $file_mp3=""; my $file_ogg=""; my $file_mpc=""; my $file_ape="";
166my $file_flac=""; my $file_cda=""; my $file_wav=""; my $file_mp4="";
167my $spinner_compress; my $label_comp; my $button_quit;
168my $spinner_compress_flac; my $fileSize = 0; my $directory;
169my $directory_final; my $directory_output = $home; my $directory_base;
170my $recursively = $true; my $check_button_recursively; my $check;  my $label;
171my $pbar; my $pbar_n; my $pbar_encode; my $progbar_files_info; my $label_pct; my $da;
172my $symbol; my $align; my $separator; my $table;
173my $orientation; my $new_val; my $ripper = 'cdparanoia';
174my $entry_l; my $entry_n; my $ComboBox_mode; my $ComboBox_freq; my $entry;
175my $status_bar; my $context_id = 0; my $adjust;
176my @files_info = (); my @music_sector; my @length = (); # array with music length of audio cd
177my %albums_already_played;   # hash whose key is only the played album name
178my %artists_already_played;  # hash whose key is only the played artist name
179my $already_normalized; my $extension_input = 'other'; my $extension_output = 'mp3';
180my $bitrate_nominal; # for ogg
181my $bitrate_constant = 160; my $bitrate_avr_mp3 = 160; my $bitrate_avr_mp4 = 160;
182my $quality = 3; # encoder algorithms process for lame.
183my $bitrate_avr_ogg = 160; my $bitrate_original; my $bitrate_average = $true;
184my $pid_lame_decode = -1; my $pid_lame_encode = -1; my $pid_rip_cda = -1;
185my $pid_mpc_decode = -1; my $pid_mpc_encode = -1; my $pid_cdpar_rip = -1;
186my $pid_normalize = -1; my $pid_ogg_decode = -1; my $pid_ogg_encode = -1;
187my $pid_ape = -1; my $pid_flac_decode = -1; my $pid_player;
188my $audio_cd_volume = 80; my $button_volume;
189my $V = 4; my $Vogg = 4; my $Vmpc = 5; my $Vmp4 = 200; my $encode="average";# constant, average or variable
190my @array_wav; my @array_ogg; my @array_of_files; #array of mp3/ogg/wav
191my $textview; my $scrolled; my $buffer; my $iter; my $track;
192my $vbox_text; my $hbox_text; my $frame_debug;
193my $count = 0; my $timer; my $window_font_name = 'Sans 10'; my $button_font;
194my %encode_type; $encode_type{'mp3'} = 'average'; $encode_type{'mp4'} = 'average';
195$encode_type{'mpc'} = 'variable'; $encode_type{'ogg'} = 'average';
196
197my %options = ( mp3 => { encode_type => 'average', # not used yet!
198                              player => 'mpg321',
199                       },
200		mp4 => { encode_type => 'average',
201                              player => 'mplayer',
202                       },
203		mpc => { encode_type => 'variable',
204                              player => 'mpg321',
205                       },
206		ogg => { encode_type => 'average',
207                              player => 'mplayer',
208                       },
209		ape => { encode_type => 'average',
210                              player => 'mplayer',
211                       },
212	       flac => { encode_type => 'variable',
213                              player => 'mpg321',
214                       },
215		wav => { encode_type => 'average',
216                              player => 'mplayer',
217                       },
218		tag => {      artist => '',
219		               album => '',
220		       }
221	      );
222
223my $check_button_del_wav; my $check_button_cddb; my $spinner_pri;
224my $go_back = $false; my $go_forward = $false; my $delete_wave = $true;
225my $discDB = $true; my $entry_CDDB_server; my $CDDB_server = 'freedb.freedb.org';
226my $spinner_cddb_port; my $cddb_port = 8880; my $cddb_transport = 'cddb';
227my $priority = 15; my $change_properties = $false;
228my $check_change_properties; my $check_button_overwrite; my $check_button_overwrite_cddb;
229my $overwrite_cddb = $false; my $entry_format_cda; my $file_format_cda_rip = "%b/%n-%a-%t";
230my $overwrite = $false; my $entry_def_comment; my $comment_tag = "gnormalize - gain=%gain";
231my $gain = 0.00; my $insensibility = 0.3; my $spinner_sensi;
232my $mode = 'joint stereo'; my $freq = 44100; my $comp_ape = 2000; my $comp_flac = 3;
233my $entry_cda; my $button_cdda1; my $button_cdda2; my $button_cdda3;
234my $paranoia = $true; my $paranoia_disable = $false; my $paranoia_no_verify = $false;
235my $change_fonte = $true; my $change_fonte_norm = $true;
236my $change_font_layout = $true; my $change_font_rand = $true;
237my $additional_command_mp3 = ""; my $additional_command_mp4 = ""; my $additional_command_mpc = "";
238my $additional_command_ogg = ""; my $additional_command_ape = ""; my $additional_command_flac = "";
239my $entry_command_mp3; my $entry_command_mp4; my $entry_command_mpc;
240my $entry_command_ogg; my $entry_command_ape; my $entry_command_flac;
241my $renderer_track; my $renderer_artist; my $renderer_title; my $renderer_album; my $renderer_length;
242my $button_info_type = 1; my $notebook_page_num = 0; my $notebook_page_num2 = 0;
243my $scrolling_text_speed = 45; # 45 milliseconds
244my $scrolling_text_incremental = 1; # 1 pixel
245my $scrolling_type = "random"; my $scrolling_text_orientation = 1;
246#---cdparanoia options
247my $disable_paranoia = $false; my $disable_extra_paranoia = $false;
248my $abort_on_skip = $false; my $never_skip_repair = $false;
249my $audio_cd_channel; my $audio_cd_total_time; my $audio_cd_fileSize;
250my $button_noia1; my $button_noia2; my $button_noia3; my $button_noia4;
251# The paths are automatically found.
252my $lame_path; my $oggenc_path; my $mpcenc_path; my $ape_path; my $flac_path;
253my $metaflac_path; my $wavegain_path; my $oggdec_path; my $mpcdec_path;
254my $cdparanoia_path; my $cdda2wav_path; my $faac_path; my $faad_path;
255my $mpg123_path; my $mpg321_path;  my $ogg123_path; my $flac123_path;
256my $mplayer_path; my $madplay_path; my $amixer_path; my $play_path;
257my $aplay_path; my $vorbiscomment_path; my $nice_path; my $audiocd;
258my $cdcd_path; my $cdplayer = "Audio::CD";
259#---mp3/ogg tag
260my $mode_channel; my $Frequency = 0; my $Time_Min_Sec; my $Technical_Info;
261my $Title; my $Comment; my $Genre; my $Artist; my $Track_Number; my $Total_Track; my $Album; my $Year;
262my %metadata = ();  # hash with metadata (tag) and technical information
263# ---- More global variables ---- cdrom player #
264my $window_cd_player; my $window_cd_player_width = 252; my $window_cd_player_height = 56;
265my $notebook_cd_player_num = 0;
266my $color_bg_cdplay = '#839975'; my $color_cdplayer_shadow = '#839975';
267my $color_cdplayer_arrows = '#651DB6'; my $color_cdplayer_shadow_motion = '#651DB6';
268my $color_cdplayer_arrows_motion = '#839975'; my $color_cdplayer_digit = '#651DB6';
269my $color_cdplayer_digit_remaining = '#FFA500';
270my $force_unselect_all_tracks = $false;
271my $show_cdplayer_skin = $false; my $show_cdplayer_border = $true; my $show_cdplayer_keep_above = $true;
272my $show_time_remaining = $false; my $show_time_abs = $false;
273my $playing_music = $false;
274my $selected_row = 0; # default audio track
275my $total_time = 0;
276my @rows_already_played = ();
277my $play_random = $false; my $loop_tracks = $false;
278my @start_sector; my $pause = $false; my $toc_audiocd;
279my $column_show_extension = $false; my $column_show_filepath = $false; my $column_show_filename = $false;
280my $column_show_frequency = $false; my $column_show_bitrate = $true; my $column_show_year = $false;
281my $column_show_artist = $true; my $column_show_album = $false; my $column_show_track = $true;
282my $column_show_file = $true; my $column_show_play_count = $true;
283
284my $use_filter = $false; my $ComboBox_player_with_filter;
285
286#---player---#
287my $device_path = "/dev/audio"; my $device_type = "alsa";
288my $player_mp3  = "mpg321";  my %all_mp3_player;
289my $player_mp4  = "mplayer"; my %all_mp4_player;
290my $player_mpc  = "mplayer"; my %all_mpc_player;
291my $player_ogg  = "mplayer"; my %all_ogg_player;
292my $player_ape  = "mplayer"; my %all_ape_player;
293my $player_flac = "mplayer"; my %all_flac_player;
294my $player_wav  = "mplayer"; my %all_wav_player;
295#--languages--#
296my $language = 'English'; my $ComboBox_Lang_5a; my %all_language; # Hash of all the different languages
297my $ComboBox_Names;  my $Names_Active_Num = 0;
298
299#-----------------------------------------------------#
300
301my $setting_file = "$home/.config/gnormalize/config";
302my $lang_dir     = "$home/.config/gnormalize/language";
303my %settings;    # Hash of all the different options that can be saved
304
305if ( not -d filename_from_unicode $lang_dir ){
306   my @cmd = ( 'mkdir', '-p', $lang_dir ); # create the directory
307   exec_cmd_system2(@cmd);
308}
309
310sub read_settings { # not yet used
311   my $file = shift;
312   if ( !-e filename_from_unicode $file ) { return; }
313   open( INPUT, '<:utf8', filename_from_unicode $file ) or die "Can't open <$file>: $!, stopped";
314   while (my $line = <INPUT>) {
315      chomp($line);
316      next if ( $line !~ /=/ );
317      (my $key, my $value) = split(/=/, $line);
318      $settings{$key} = $value;
319   }
320   close( INPUT );
321   #foreach my $key ( sort keys %settings ) { print $key, '=', $settings{$key}, "\n"; }
322}
323# read_settings($setting_file);
324
325sub save_settings { # not yet used
326   #$lang_dir/language.txt
327   $settings{'cdrom'} = $entry_cda->get_text;
328
329   my $key;
330   open( OUTPUT, '>:utf8', $setting_file ) or die "Couldn't open $setting_file for writing: $!\n";
331   foreach $key ( sort keys %settings ) { print OUTPUT $key, '=', $settings{$key}, "\n"; }
332   close( OUTPUT );
333}
334
335#######------------- Read the config file -----------#######
336
337sub read_config {
338	my $config = filename_to_unicode $setting_file;
339
340	if( -r filename_from_unicode $config ) {
341	   my $file_number = 1;
342	   # read config file
343	   open(CONFIG, '<:utf8', $config );
344	   while( <CONFIG> )
345	   {
346	      if ($_ =~ /^cdrom=(.*)/){ $audiodevice_path = $1; }
347	      if ($_ =~ /^recursively=(.*)/){ $recursively = $1; }
348	      if ($_ =~ /^extension_output=(.*)/){ $extension_output = $1; }
349	      if ($_ =~ /^directory_output=(.*)/){ $directory_output = $1; }
350	      if ($_ =~ /^normalization_type=(Track|Album|None)$/){ $norm_type = $1; }
351	      if ($_ =~ /^gain=(.*)/){ $gain = $1; }
352	      if ($_ =~ /^insensibility=(.*)/){ $insensibility = $1; }
353	      if ($_ =~ /^delete_wave=(.*)/){ $delete_wave = $1; }
354	      if ($_ =~ /^discDB=(.*)/){ $discDB = $1; }
355	      if ($_ =~ /^overwrite_cddb=(.*)/){ $overwrite_cddb = $1; }
356	      if ($_ =~ /^CDDB_server=(.*)/){ $CDDB_server = $1; }
357	      if ($_ =~ /^cddb_port=(\d+)/){ $cddb_port = $1; }
358	      if ($_ =~ /^cddb_transport=(.*)/){ $cddb_transport = $1; }
359	      if ($_ =~ /^priority=(.*)/){ $priority = $1; }
360	      if ($_ =~ /^change_properties=(.*)/){ $change_properties = $1; }
361	      if ($_ =~ /^show_all_tooltips=([01])/){ $show_all_tooltips = $1; }
362	      if ($_ =~ /^file_format_cda_rip=(.*)/){ $file_format_cda_rip = $1; }
363	      if ($_ =~ /^overwrite=(.*)/){ $overwrite = $1; }
364	      if ($_ =~ /^comment_tag=(.*)/){ $comment_tag = $1; }
365	      if ($_ =~ /^additional_command_mp3=(.*)/){ $additional_command_mp3 = $1; }
366	      if ($_ =~ /^additional_command_mp4=(.*)/){ $additional_command_mp4 = $1; }
367	      if ($_ =~ /^additional_command_mpc=(.*)/){ $additional_command_mpc = $1; }
368	      if ($_ =~ /^additional_command_ogg=(.*)/){ $additional_command_ogg = $1; }
369	      if ($_ =~ /^additional_command_ape=(.*)/){ $additional_command_ape = $1; }
370	      if ($_ =~ /^additional_command_flac=(.*)/){ $additional_command_flac = $1; }
371	      if ($_ =~ /^character=(.*)/){ $id3tag_character = $1; }
372	      if ($_ =~ /^cdplayer=(.*)/){ $cdplayer = $1; }
373	      if ($_ =~ /^window_font_name=(.*)/){ $window_font_name = $1; }
374	      if ($_ =~ /^ripper=(.*)/){ $ripper = $1; }
375	      if ($_ =~ /^language=(.*)/){ $language = $1; }
376	      if ($_ =~ /^Names_Active_Num=(\d+)/){ $Names_Active_Num = $1; }
377	      if ($_ =~ /^encode=(.*)/){ $encode = $1; }
378	      if ($_ =~ /^encode_remember_mp3=(.*)/){ $encode_type{mp3} = $1; }
379	      if ($_ =~ /^encode_remember_mp4=(.*)/){ $encode_type{mp4} = $1; }
380	      if ($_ =~ /^encode_remember_mpc=(.*)/){ $encode_type{mpc} = $1; }
381	      if ($_ =~ /^encode_remember_ogg=(.*)/){ $encode_type{ogg} = $1; }
382	      if ($_ =~ /^bitrate=(.*)/){ $bitrate_constant = $1; }
383	      if ($_ =~ /^bitrate_avr_mp3=(.*)/){ $bitrate_avr_mp3 = $1; }
384	      if ($_ =~ /^bitrate_avr_mp4=(.*)/){ $bitrate_avr_mp4 = $1; }
385	      if ($_ =~ /^bitrate_avr_ogg=(.*)/){ $bitrate_avr_ogg = $1; }
386	      if ($_ =~ /^bitrate_vrb_mp3=(.*)/){ $V = $1; }
387	      if ($_ =~ /^bitrate_vrb_mp4=(.*)/){ $Vmp4 = $1; }
388	      if ($_ =~ /^bitrate_vrb_ogg=(.*)/){ $Vogg = $1; }
389	      if ($_ =~ /^bitrate_vrb_mpc=(.*)/){ $Vmpc = $1; }
390	      if ($_ =~ /^comp_ape=(.*)/){ $comp_ape = $1; }
391	      if ($_ =~ /^comp_flac=(.*)/){ $comp_flac = $1; }
392	      if ($_ =~ /^mode=(.*)/){ $mode = $1; }
393	      if ($_ =~ /^Min=(.*)/){ $vb_Min = $1; }
394	      if ($_ =~ /^Max=(.*)/){ $vb_Max = $1; }
395	      if ($_ =~ /^freq=(8000|11025|12000|16000|22050|24000|32000|44100|48000)$/){ $freq = $1; }
396	      if ($_ =~ /^quality=(.*)/){ $quality = $1; }
397	      if ($_ =~ /^disable_paranoia=(.*)/){ $disable_paranoia = $1; }
398	      if ($_ =~ /^never_skip_repair=(.*)/){ $never_skip_repair = $1; }
399	      if ($_ =~ /^disable_extra_paranoia=(.*)/){ $disable_extra_paranoia = $1; }
400	      if ($_ =~ /^abort_on_skip=(.*)/){ $abort_on_skip = $1; }
401	      if ($_ =~ /^paranoia=(.*)/){ $paranoia = $1; }
402	      if ($_ =~ /^paranoia_disable=(.*)/){ $paranoia_disable = $1; }
403	      if ($_ =~ /^paranoia_no_verify=(.*)/){ $paranoia_no_verify = $1; }
404	      if ($_ =~ /^play_random=(.*)/){ $play_random = $1; }
405	      if ($_ =~ /^loop_tracks=(.*)/){ $loop_tracks = $1; }
406	      if ($_ =~ /^audio_cd_volume=(\d*)/){ $audio_cd_volume = $1; }
407	      if ($_ =~ /^window_width=(\d+)/){ $window_width = $1; }
408	      if ($_ =~ /^window_height=(\d+)/){ $window_height = $1; }
409	      if ($_ =~ /^window_cd_player_width=(\d+)/){ $window_cd_player_width = $1; }
410	      if ($_ =~ /^window_cd_player_height=(\d+)/){ $window_cd_player_height = $1; }
411	      if ($_ =~ /^show_tux_animation=([01])/){ $show_tux_animation = $1; }
412	      if ($_ =~ /^color_bg_cdplay=(#[ABCDEFabcdef\d]{6})/){ $color_bg_cdplay = $1; }
413	      if ($_ =~ /^color_cdplayer_shadow=(#[ABCDEFabcdef\d]{6})/){ $color_cdplayer_shadow = $1; }
414	      if ($_ =~ /^color_cdplayer_arrows=(#[ABCDEFabcdef\d]{6})/){ $color_cdplayer_arrows = $1; }
415	      if ($_ =~ /^color_cdplayer_shadow_motion=(#[ABCDEFabcdef\d]{6})/){ $color_cdplayer_shadow_motion = $1; }
416	      if ($_ =~ /^color_cdplayer_arrows_motion=(#[ABCDEFabcdef\d]{6})/){ $color_cdplayer_arrows_motion = $1; }
417	      if ($_ =~ /^color_cdplayer_digit=(#[ABCDEFabcdef\d]{6})/){ $color_cdplayer_digit = $1; }
418	      if ($_ =~ /^color_cdplayer_digit_remaining=(#[ABCDEFabcdef\d]{6})/){ $color_cdplayer_digit_remaining = $1; }
419	      if ($_ =~ /^scrolling_text_speed=(\d+)/){ $scrolling_text_speed = $1 if ($1>=10); }
420	      if ($_ =~ /^scrolling_text_incremental=(\d+)/){ $scrolling_text_incremental = $1; }
421	      if ($_ =~ /^scrolling_type=(.*)/){ $scrolling_type = $1; }
422	      if ($_ =~ /^scrolling_text_orientation=((-1|1))/){ $scrolling_text_orientation = $1; }
423	      if ($_ =~ /^show_cdplayer_skin=([01])/){ $show_cdplayer_skin = $1; }
424	      if ($_ =~ /^show_cdplayer_border=([01])/){ $show_cdplayer_border = $1; }
425	      if ($_ =~ /^show_cdplayer_keep_above=([01])/){ $show_cdplayer_keep_above = $1; }
426	      if ($_ =~ /^player_mp3=(madplay|mpg123|mpg321|mplayer)$/){ $player_mp3 = $1; }
427	      if ($_ =~ /^player_mp4=(mplayer)$/){ $player_mp4 = $1; }
428	      if ($_ =~ /^player_mpc=(mpcdec|mplayer)$/){ $player_mpc = $1; }
429	      if ($_ =~ /^player_ogg=(ogg123|mplayer)$/){ $player_ogg = $1; }
430	      if ($_ =~ /^player_ape=(mac|mplayer|aplay)$/){ $player_ape = $1; }
431	      if ($_ =~ /^player_flac=(flac123|mplayer)$/){ $player_flac = $1; }
432	      if ($_ =~ /^player_wav=(mplayer|aplay|play)$/){ $player_wav = $1; }
433	      if ($_ =~ /^device_path=(.*)/){ $device_path = $1; }
434	      if ($_ =~ /^device_type=(.*)/){ $device_type = $1; }
435	      if ($_ =~ /^use_filter=(\d+)/){ $use_filter = $1; }
436	      if ($_ =~ /^column_show_file=(0|1)/){ $column_show_file = $1; }
437	      if ($_ =~ /^column_show_artist=(0|1)/){ $column_show_artist = $1; }
438	      if ($_ =~ /^column_show_album=(0|1)/){ $column_show_album = $1; }
439	      if ($_ =~ /^column_show_track=(0|1)/){ $column_show_track = $1; }
440	      if ($_ =~ /^column_show_extension=(0|1)/){ $column_show_extension = $1; }
441	      if ($_ =~ /^column_show_play_count=(0|1)/){ $column_show_play_count = $1; }
442	      if ($_ =~ /^column_show_filepath=(0|1)/){ $column_show_filepath = $1; }
443	      if ($_ =~ /^column_show_filename=(0|1)/){ $column_show_filename = $1; }
444	      if ($_ =~ /^column_show_frequency=(0|1)/){ $column_show_frequency = $1; }
445	      if ($_ =~ /^column_show_bitrate=(0|1)/){ $column_show_bitrate = $1; }
446	      if ($_ =~ /^column_show_year=(0|1)/){ $column_show_year = $1; }
447	      if ($_ =~ /^button_info_type=(\d)/){ $button_info_type = $1; }
448	      if ($_ =~ /^notebook_page_num=(\d+)/){ $notebook_page_num = $1; }
449	      if ($_ =~ /^notebook_page_num2=(\d+)/){ $notebook_page_num2 = $1; }
450	      if ($_ =~ /^notebook_cd_player_num=(\d+)/){ $notebook_cd_player_num = $1; }
451
452	      # read the music list
453	      if ($_ =~ /^checked=(0|1):::file=(\d+):::track=(.*):::artist=(.*):::title=(.*):::album=(.*):::length=(.*):::bitrate=(\d+):::year=(.*):::frequency=(\d+):::genre=(.*):::filepath=(.*):::directory_remain=(.*):::filename=(.*):::extension_input=(.*)$/){
454                 my $filepath = $12; # print "\$filepath = $filepath\n";
455
456		 eval { if (not -e filename_from_unicode $filepath){warn filename_from_unicode "\nCould not find <$filepath>: $!"; next;} };
457
458		 push @files_info, {   # fill the array of array references
459                                       rip         => $1,
460	                               file        => $file_number, # file_number = row - 1
461			               track       => $3,
462			               artist      => $4,
463			               title       => $5,
464			               album       => $6,
465			               length      => $7,
466			               bitrate     => $8,
467				       year        => $9,
468				       frequency   => $10,
469				       genre       => $11,
470
471				       filepath          => $filepath,  # $12,
472				       directory_remain  => $13,
473				       filename          => $14,
474			               extension_input   => $15,
475                                   };
476	         $file_number += 1;
477	      }
478	}
479	close(CONFIG);
480	$extension_input = $files_info[0]{extension_input} if ( defined $files_info[0]{extension_input} );
481     }
482     $gain = number_value($gain);
483     return;
484}
485
486#######------------- Save the config file -----------#######
487
488# save configurations only when quit the program
489sub save_config {
490	my $config = $setting_file;
491	if ( not -e filename_from_unicode $config ){ print "Save configuration file: $config\n"; }
492
493	# test_expr ? if_true_expr : if_false_expr
494	open(CONFIG, '>:utf8',  filename_from_unicode $config ) or die "Can not write to <$config>: $!";
495	  print CONFIG "   gnormalize - $VERSION\n";
496	  print CONFIG "author: $AUTHOR\n";
497	  print CONFIG "homepage: $HOMEPAGE\n\n";
498	  print CONFIG "cdrom=",$entry_cda->get_text,"\n";
499	  print CONFIG "recursively=$recursively\n";
500	  print CONFIG "extension_output=$extension_output\n";
501	  print CONFIG "directory_output=$directory_output\n";
502	  print CONFIG "normalization_type=$norm_type\n";
503	  print CONFIG "gain=",$spinner->get_value,"\n";
504	  print CONFIG "insensibility=",$spinner_sensi->get_value,"\n";
505	  print CONFIG "delete_wave=",$check_button_del_wav->get_active?1:0,"\n";
506	  print CONFIG "discDB=",$check_button_cddb->get_active?1:0,"\n";
507	  print CONFIG "overwrite_cddb=",$check_button_overwrite_cddb->get_active?1:0,"\n";
508	  print CONFIG "CDDB_server=",$entry_CDDB_server->get_text,"\n";
509	  print CONFIG "cddb_port=",$spinner_cddb_port->get_value,"\n";
510	  print CONFIG "cddb_transport=$cddb_transport\n";
511	  print CONFIG "priority=",$spinner_pri->get_value,"\n";
512	  print CONFIG "change_properties=",$check_change_properties->get_active?1:0,"\n";
513	  print CONFIG "show_all_tooltips=",$check_button_tooltips->get_active?1:0,"\n";
514	  print CONFIG "file_format_cda_rip=",$entry_format_cda->get_text,"\n";
515	  print CONFIG "overwrite=",$check_button_overwrite->get_active?1:0,"\n";
516	  print CONFIG "comment_tag=",$entry_def_comment->get_text,"\n";
517	  print CONFIG "additional_command_mp3=",$entry_command_mp3->get_text,"\n";
518	  print CONFIG "additional_command_mp4=",$entry_command_mp4->get_text,"\n";
519	  print CONFIG "additional_command_mpc=",$entry_command_mpc->get_text,"\n";
520	  print CONFIG "additional_command_ogg=",$entry_command_ogg->get_text,"\n";
521	  print CONFIG "additional_command_ape=",$entry_command_ape->get_text,"\n";
522	  print CONFIG "additional_command_flac=",$entry_command_flac->get_text,"\n";
523	  print CONFIG "character=",$id3tag_character,"\n";
524	  print CONFIG "cdplayer=",$cdplayer,"\n";
525	  print CONFIG "window_font_name=",$button_font->get_label,"\n";
526	  print CONFIG "ripper=",$ripper,"\n";
527	  print CONFIG "language=",$ComboBox_Lang_5a->get_active_text,"\n";
528	  print CONFIG "Names_Active_Num=",$ComboBox_Names->get_active,"\n";
529	  print CONFIG "encode=",$ComboBox_encode_type->get_active_text,"\n";
530	  print CONFIG "encode_remember_mp3=$encode_type{mp3}\n";
531	  print CONFIG "encode_remember_mp4=$encode_type{mp4}\n";
532	  print CONFIG "encode_remember_mpc=$encode_type{mpc}\n";
533	  print CONFIG "encode_remember_ogg=$encode_type{ogg}\n";
534	  print CONFIG "bitrate=$bitrate_constant\n";
535	  print CONFIG "bitrate_avr_mp3=$bitrate_avr_mp3\n";
536	  print CONFIG "bitrate_avr_mp4=$bitrate_avr_mp4\n";
537	  print CONFIG "bitrate_avr_ogg=$bitrate_avr_ogg\n";
538	  print CONFIG "bitrate_vrb_mp3=",$spinner_V->get_value,"\n";
539	  print CONFIG "bitrate_vrb_mp4=",$spinner_Vmp4->get_value,"\n";
540	  print CONFIG "bitrate_vrb_ogg=", number_value(sprintf("%.1f", $spinner_Vogg->get_value)) ,"\n";
541	  print CONFIG "bitrate_vrb_mpc=",$spinner_Vmpc->get_value,"\n";
542	  print CONFIG "comp_ape=",$spinner_compress->get_value,"\n";
543	  print CONFIG "comp_flac=",$spinner_compress_flac->get_value,"\n";
544	  print CONFIG "mode=",$ComboBox_mode->get_active_text,"\n";
545	  print CONFIG "Min=",$ComboBox_Min->get_active_text,"\n";
546	  print CONFIG "Max=",$ComboBox_Max->get_active_text,"\n";
547	  print CONFIG "freq=",$ComboBox_freq->get_active_text,"\n";
548	  print CONFIG "quality=$quality\n";
549	  print CONFIG "\n---cdparanoia options:\n";
550	  print CONFIG "disable_paranoia=",$button_noia1->get_active?1:0,"\n";
551	  print CONFIG "never_skip_repair=",$button_noia2->get_active?1:0,"\n";
552	  print CONFIG "disable_extra_paranoia=",$button_noia3->get_active?1:0,"\n";
553	  print CONFIG "abort_on_skip=",$button_noia4->get_active?1:0,"\n";
554	  print CONFIG "\n---cdda2wav options:\n";
555	  print CONFIG "paranoia=",$button_cdda1->get_active?1:0,"\n";
556	  print CONFIG "paranoia_disable=",$button_cdda2->get_active?1:0,"\n";
557	  print CONFIG "paranoia_no_verify=",$button_cdda3->get_active?1:0,"\n\n";
558	  print CONFIG "play_random=$play_random\n";
559	  print CONFIG "loop_tracks=$loop_tracks\n";
560	  print CONFIG "audio_cd_volume=$audio_cd_volume\n";
561	  print CONFIG "\n---window size:\n";
562	  # see resize_window() subroutine
563	  print CONFIG "window_width=",int($window->allocation->width),"\n";
564	  print CONFIG "window_height=",$button_info_type != 2 ?
565	               int($window->allocation->height) : int($window->allocation->height - $frame_debug->allocation->height) ,"\n";
566	  print CONFIG "window_cd_player_width=$window_cd_player_width\n";
567	  print CONFIG "window_cd_player_height=$window_cd_player_height\n";
568	  print CONFIG "show_tux_animation=",$check_button_animation->get_active?1:0,"\n";
569	  print CONFIG "\n---cd player color:\n";
570	  print CONFIG "color_bg_cdplay=$color_bg_cdplay\n";
571	  print CONFIG "color_cdplayer_shadow=$color_cdplayer_shadow\n";
572	  print CONFIG "color_cdplayer_arrows=$color_cdplayer_arrows\n";
573	  print CONFIG "color_cdplayer_shadow_motion=$color_cdplayer_shadow_motion\n";
574	  print CONFIG "color_cdplayer_arrows_motion=$color_cdplayer_arrows_motion\n";
575	  print CONFIG "color_cdplayer_digit=$color_cdplayer_digit\n";
576	  print CONFIG "color_cdplayer_digit_remaining=$color_cdplayer_digit_remaining\n";
577	  print CONFIG "scrolling_text_speed=$scrolling_text_speed\n";
578	  print CONFIG "scrolling_text_incremental=$scrolling_text_incremental\n";
579	  print CONFIG "scrolling_type=$scrolling_type\n";
580	  print CONFIG "scrolling_text_orientation=$scrolling_text_orientation\n";
581	  print CONFIG "show_cdplayer_skin=",$show_cdplayer_skin?1:0,"\n";
582	  print CONFIG "show_cdplayer_border=",$show_cdplayer_border?1:0,"\n";
583	  print CONFIG "show_cdplayer_keep_above=",$show_cdplayer_keep_above?1:0,"\n";
584	  print CONFIG "\n---players:\n";
585	  print CONFIG "player_mp3=$player_mp3\n";
586	  print CONFIG "player_mp4=$player_mp4\n";
587	  print CONFIG "player_mpc=$player_mpc\n";
588	  print CONFIG "player_ogg=$player_ogg\n";
589	  print CONFIG "player_ape=$player_ape\n";
590	  print CONFIG "player_flac=$player_flac\n";
591	  print CONFIG "player_wav=$player_wav\n";
592	  print CONFIG "device_path=$device_path\n";
593	  print CONFIG "device_type=$device_type\n";
594	  print CONFIG "use_filter=$use_filter\n";
595	  print CONFIG "\n---columns:\n";
596	  print CONFIG "column_show_file=",$column_show_file?1:0,"\n";
597	  print CONFIG "column_show_artist=",$column_show_artist?1:0,"\n";
598	  print CONFIG "column_show_album=",$column_show_album?1:0,"\n";
599	  print CONFIG "column_show_track=",$column_show_track?1:0,"\n";
600	  print CONFIG "column_show_extension=",$column_show_extension?1:0,"\n";
601	  print CONFIG "column_show_play_count=",$column_show_play_count?1:0,"\n";
602	  print CONFIG "column_show_filepath=",$column_show_filepath?1:0,"\n";
603	  print CONFIG "column_show_filename=",$column_show_filename?1:0,"\n";
604	  print CONFIG "column_show_frequency=",$column_show_frequency?1:0,"\n";
605	  print CONFIG "column_show_bitrate=",$column_show_bitrate?1:0,"\n";
606	  print CONFIG "column_show_year=",$column_show_year?1:0,"\n";
607	  print CONFIG "button_info_type=",$button_info_type,"\n";
608	  print CONFIG "notebook_page_num=",$notebook->get_current_page,"\n";
609	  print CONFIG "notebook_page_num2=",$notebook2->get_current_page,"\n";
610	  print CONFIG "notebook_cd_player_num=$notebook_cd_player_num\n";
611
612	  print CONFIG "\n---music list:\n";
613
614	  # save list store
615          foreach my $d (@files_info) {    # print "\$d->{file} = $d->{file} ; n = ",$#files_info+1,"\n";
616	     return if not defined $files_info[0]{artist};
617	     next if ($d->{extension_input} eq 'cda' or $d->{remove});
618             print CONFIG "checked=$d->{rip}:::file=$d->{file}:::track=$d->{track}:::artist=$d->{artist}:::".
619	                  "title=$d->{title}:::album=$d->{album}:::length=$d->{length}:::bitrate=$d->{bitrate}:::".
620			  "year=$d->{year}:::frequency=$d->{frequency}:::genre=$d->{genre}:::filepath=$d->{filepath}:::".
621			  "directory_remain=$d->{directory_remain}:::filename=$d->{filename}:::extension_input=$d->{extension_input}\n";
622          }
623	close CONFIG;
624}
625
626read_config();
627#save_config(); # save when quit; see 'sub Quit'
628#print "extension_output = $extension_output \n";
629
630#######------------------- Icon --------------------########
631
632# from /usr/share/doc/perl-Gtk2-1.054/gtk-demo/main.pl
633sub set_icon {
634  my $icon_path = shift || "/usr/share/icons/gnormalize.png";
635  my $pixbuf;
636  eval { $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($icon_path); };
637  #eval { $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file("/usr/share/icons/wolf.png"); };
638  # "/usr/share/icons/sound_section.png"
639  if ($pixbuf) {
640    # The icon has a white background, make it transparent
641    my $transparent = $pixbuf->add_alpha ($true, 0xff, 0xff, 0xff);
642
643    # only one item on the parameter list, but the parameter list is a list
644    Gtk2::Window->set_default_icon_list ($transparent);
645    #Gtk2::Window->set_default_icon_list ($pixbuf);
646  }
647}
648
649set_icon ();
650
651#######---------------- Tranlation -----------------########
652
653my %langs; # Hash of all names, tabs, tips and messages
654
655# All defined language - hash of all the different languages
656$all_language{'English'}    = $true;
657$all_language{'Portugu�s'}  = $true;
658$all_language{'Fran�ais'}   = $true;
659$all_language{'Nederlands'} = $true;
660$all_language{'Espag�ol'}   = $true;
661$all_language{'Deutsch'}    = $true;
662#$all_language{'new_language'}    = $true;  # <-- add a new language here
663
664read_new_langs();
665
666$language = 'English' if not defined $all_language{"$language"}; # standard language
667
668sub set_language {
669
670   if ( $language eq 'English' )
671   {
672   $langs{all_tab01} = "Data";
673   $langs{all_tab02} = "Configuration";
674   $langs{all_tab03} = "Information";
675   $langs{all_tab04} = "Rip/Play";
676   $langs{all_tab05} = "Translations";
677   $langs{all_tab06} = "About";
678   $langs{all_tab07} = "Option1";
679   $langs{all_tab08} = "Option2";
680   $langs{all_tab09} = "CDDB";
681   $langs{all_tab10} = "Ripper";
682   $langs{all_tab11} = "Colors";
683   $langs{all_tab12} = "Players";
684   $langs{all_tab13} = "Columns";
685   $langs{all_tab14} = "Skins";
686
687   $langs{name001} = "Normalization Type";
688   $langs{name002} = "Insensibility";
689   $langs{name003} = "Selected File";
690   $langs{name004} = "Input Directory";
691   $langs{name005} = "Output: Lossy Compression";
692   $langs{name006} = "Output: Lossless Compression";
693   $langs{name106} = "Output: Uncompressed";
694
695   $langs{name007} = "File";
696   $langs{name008} = "Recursively";
697   $langs{name009} = "Delete Wav Files";
698   $langs{name010} = "Change Properties";
699   $langs{name011} = "Overwrite";
700   $langs{name012} = "Priority";
701   $langs{name013} = "Add Command";
702   $langs{name014} = "Filename Rip";
703   $langs{name015} = "Character";
704   $langs{name016} = "Language";
705   $langs{name017} = "Font";
706   $langs{name018} = "Read CDDB";
707   $langs{name019} = "Overwrite CDDB";
708   $langs{name020} = "CDDB Server";
709   $langs{name021} = "Port";
710   $langs{name022} = "Transport";
711   $langs{name023} = "Disable Paranoia";
712   $langs{name024} = "Disable Extra Paranoia";
713   $langs{name025} = "Abort on Skip";
714   $langs{name026} = "Never Skip Repair";
715   $langs{name027} = "Paranoia No-Verify";
716   $langs{name028} = "Paranoia Disable";
717   $langs{name029} = "Ripper";
718   $langs{name030} = "Encode Type";
719   $langs{name031} = "Mode";
720   $langs{name032} = "Encode Quality";
721   $langs{name033} = "Variable";
722   $langs{name034} = "Title";
723   $langs{name035} = "Artist";
724   $langs{name036} = "Album"; ## <<<-----
725   $langs{name037} = "Comment";
726   $langs{name038} = "Year";
727   $langs{name039} = "Track Number";
728   $langs{name040} = "Genre";
729   $langs{name041} = "Frequency";
730   $langs{name042} = "Time";
731   $langs{name043} = "Channels";
732   $langs{name044} = "Length";
733   $langs{name045} = "Track";
734   $langs{name046} = "Artist";
735   $langs{name047} = "Title";
736   $langs{name048} = "Size";
737   $langs{name049} = "Year";
738   $langs{name050} = "Filepath";
739   $langs{name051} = "Extension";
740   $langs{name052} = "Output Directory";
741   $langs{name053} = "Album"; ## <<<-----
742   $langs{name054} = "Play Count";
743   $langs{name055} = "Animation";
744   $langs{name056} = "Filename";
745   $langs{name057} = "Show Tooltips";
746   $langs{name058} = "Guidance Language";
747   $langs{name059} = "Current Translation";
748   $langs{name060} = "Improve The Currente Language Translation";
749   $langs{name061} = "Add New Language";
750   $langs{name062} = "Add new language here and then press save buton.";
751   $langs{name063} = "Normalize";
752   $langs{name064} = "CD Player";
753   $langs{name065} = "Play";
754
755   $langs{name429} = 'Play Tracks Without Any Filters';
756   $langs{name430} = 'Play Tracks From Different Albums';
757   $langs{name431} = 'Play Tracks From Different Artists';
758   $langs{name432} = 'Play Tracks From Different Genres';
759   $langs{name433} = 'Play Tracks From Different Years';
760
761   $langs{tip101}  = "Track: use ReplayGain Single Track gain setting.\nAlbum: don't implemented yet!\n";
762   $langs{tip101} .= "None: no normalization is done.\n";
763   $langs{tip101} .= "---*---\n";
764   $langs{tip101} .= "gnormalize uses the wavegain to normalize wave files.\n";
765   $langs{tip101} .= "wavegain is a program that applies ReplayGain normalization algorithms directly to wave files.";
766   $langs{tip102}  = "Apply additional Manual Gain adjustment in decibels between -12.0 and +12.0. ";
767   $langs{tip102} .= "Gain = 0.00dB correspond to 89dB standard or reference value.\n";
768   $langs{tip102} .= "---*---\n";
769   $langs{tip102} .= "Basic ideas of ReplayGain normalization algorithms:\n";
770   $langs{tip102} .= "1. Calculate the Root Mean Square (RMS) of the waveform every 50ms;\n";
771   $langs{tip102} .= "2. Sort the RMS values into numerical order, and choose the value 95% up the list to represent the overall perceived loudness;\n";
772   $langs{tip102} .= "3. Calibrate the energy of a digital signal to 89dB standard value and apply the gain adjustment.\n";
773   $langs{tip102} .= "---*---\n";
774   $langs{tip102} .= "RMS(A) = sqrt(mean(A^2)) is the Root Mean Square of sound amplitude A.\n";
775   $langs{tip102} .= "Gain(A) = 2*10*log(A/A0), such that A is the measured amplitude and A0 is the reference amplitude.";
776   $langs{tip103}  = "Insensibility of normalize. Only not re-encode if:\n";
777   $langs{tip103} .= "1) the extension of input file is the same of output file;\n";
778   $langs{tip103} .= "2) and \"$langs{name010}\" button is not active;\n";
779   $langs{tip103} .= "3) and adjust of normalize is less than insensibility value.";
780   $langs{tip104}  = "Select one directory with supported file.";
781   $langs{tip105}  = "Choose the directory where all the files will be saved.";
782   $langs{tip106}  = "Normalize/Convert all supported files inside the directory (recursively or not).\n";
783   $langs{tip107}  = "Select a directory including subdirectories: descende recursively into the directory preserving the same structure.";
784   $langs{tip108}  = "After normalize, the output format will be mp3. \nPlay it with mpg123, mpg321 or madplay.";
785   $langs{tip109}  = "After normalize, the output format will be mp4 (also know as m4a) or aac. \nPlay it with mplayer.";
786   $langs{tip110}  = "After normalize, the output format will be mpc (also know as mpp or mp+). \nPlay it with mppdec or mplayer.";
787   $langs{tip111}  = "After normalize, the output format will be ogg. \nPlay it with ogg123.";
788   $langs{tip112}  = "After normalize, the output format will be ape. \nPlay it with the command:\n   mac \"song.ape\"  - -d | play -t wav - ";
789   $langs{tip113}  = "After normalize, the output format will be flac. \nPlay it with flac123 or mplayer.\nRequired flac version >= \"1.1.1\".";
790   $langs{tip114}  = "This is suitable to make an audio cd.\nWav is an uncompressed audio format.";
791   $langs{tip115}  = "NOTE:\n";
792   $langs{tip115} .= "This compression technique is known as 'Lossy Compression' whereby during the compression data can be lost. ";
793   $langs{tip115} .= "The degree of loss varies upon settings. Although data loss does occur, it is minimal and certainly not noticable ";
794   $langs{tip115} .= "when using good settings.\nExamples of Lossy compression audio: Mp3, Mp4, Mpc and Ogg.";
795   $langs{tip116}  = "NOTE:\n";
796   $langs{tip116} .= "This compression technique is known as 'Lossless Compression' in which all of the information in the original ";
797   $langs{tip116} .= "data is preserved, and the original data may be recovered in form identical to its original form.\n";
798   $langs{tip116} .= "Examples of Lossless compression audio: Ape and Flac.";
799   $langs{tip117}  = "Show the progress bar.";
800   $langs{tip118}  = "Hide the progress bar.";
801   $langs{tip119}  = "Show the command line and others info.";
802   $langs{tip120}  = "Hide the command line and others info.";
803   $langs{tip121}  = "Quit.";
804   $langs{tip122}  = "Clear all messages.";
805
806   $langs{tip201}  = "After the normalize process, the temporary wav files will be deleted.";
807   $langs{tip202}  = "After normalize the files, you can change its encode properties by determining the encode type, the bitrate of all ";
808   $langs{tip202} .= "output files, etc. Otherwise, the bitrate of each input file and others properties will be conserved.";
809   $langs{tip203}  = "The normalized file will be saved in the same directory of input. If it already exist then overwrite the original file.";
810   $langs{tip204}  = "Show the hints and informations.";
811   $langs{tip205}  = "Show the Tux and others penguins animations.";
812   $langs{tip206}  = "Modify scheduling priority: 0 (highest priority) to 19 (lowest)";
813   $langs{tip207}  = "Output filename format used only for rip Audio CD:\n";
814   $langs{tip207} .= "   %a - artist\n";
815   $langs{tip207} .= "   %b - album\n";
816   $langs{tip207} .= "   %t - song title\n";
817   $langs{tip207} .= "   %n - track number\n";
818   $langs{tip207} .= "   %y - year\n";
819   $langs{tip207} .= "   %g - genre";
820   $langs{tip208}  = "Advised font: Sans 10.";
821   $langs{tip209}  = "Read the CDDB info for Audio CD in your drive from CDDB server \"$CDDB_server\" or from files saved into $home/.cddd/.";
822   $langs{tip210}  = "Always get CDDB info from server and overwrite the CDDB file saved at $home/.cddd/";
823   $langs{tip211}  = "Some servers:\nfreedb.freedb.org:8880\nus.freedb.org:8880\ncddb.com\nus.cddb.com:8880\nsunsite.unc.edu:8880";
824   $langs{tip212}  = "Choose the port number.";
825   $langs{tip213}  = "Disable all data verification and correction features. Consequently the extraction/rip is more fast. ";
826   $langs{tip213} .= "This option implies that -Y is active. See cdparanoia (option -Z).";
827   $langs{tip214}  = "Do not accept any skips; retry forever if needed. See cdparanoia (option -z).";
828   $langs{tip215}  = "Disables intra-read data verification; only overlap checking at read boundaries is performed. Not recommended. See cdparanoia (option -Y).\n";
829   $langs{tip215} .= "Boundaries are spaces/limit inter tracks of an audio cd. cdparanoia reads this boundaries carefully when rip on track.";
830   $langs{tip216}  = "If the read skips due to imperfect data, a scratch, whatever, abort reading this track. See cdparanoia (option -X).";
831   $langs{tip217}  = "Use the paranoia library instead of cdda2wav's routines for reading. See cdda2wav (option -paranoia).";
832   $langs{tip218}  = "Disables paranoia mode. Paranoia is still being used. See cdda2wav (option -paraopts=help).";
833   $langs{tip219}  = "Switches verify off, and overlap on.";
834   $langs{tip220}  = "Choose a targeted average bitrate. Sets encoding to the bitrate closest to this value (in Kb/s).";
835   $langs{tip221}  = "Adjust the quality of mp3; 9 is the worst quality, 0 is the best quality with more Kb/s. See: man lame (option: -V).";
836   $langs{tip222}  = "Adjust the quality of encode for ogg; 10 is the best quality with more Kb/s used, -1 is the worst quality. See: man oggenc.";
837   $langs{tip223}  = "Adjust the quality of lame encoder for mp3 files: 0 is the best quality with maximum ";
838   $langs{tip223} .= "algorithms process; 9 is the worst quality but more fast. See: man lame (option: -q).";
839   $langs{tip224}  = "Set default variable bitrate (VBR) quantizer quality in percent for mp4 format; 500 is the best ";
840   $langs{tip224} .= "quality with more Kb/s used; 10 is the worst quality. See: <faac --help>.";
841   $langs{tip225}  =  "Adjust the quality of encode for mpc using profiles; 8 (braindead) is the best quality with more Kb/s used,";
842   $langs{tip225} .= " 2 (telephone) is the worst quality. See: mppenc --longhelp\n";
843   $langs{tip225} .= " q=2 --telephone - lowest quality,       (32... 48 kbps)\n";
844   $langs{tip225} .= " q=3 --thumb     - low quality/internet, (58... 86 kbps)\n";
845   $langs{tip225} .= " q=4 --radio     - medium (MP3) quality, (112...152 kbps)\n";
846   $langs{tip225} .= " q=5 --standard  - high quality (dflt),  (142...184 kbps)\n";
847   $langs{tip225} .= " q=6 --xtreme    - extreme high quality, (168...212 kbps)\n";
848   $langs{tip225} .= " q=7 --insane    - extreme high quality, (232...268 kbps)\n";
849   $langs{tip225} .= " q=8 --braindead - extreme high quality, (232...278 kbps)";
850   $langs{tip226}  = "Compression level:\n  fast: 1000\n  normal: 2000\n  high: 3000\n  extra high: 4000\n  insane: 5000\n";
851   $langs{tip226} .= "\nMore compression level \nresults small files.\nSee the help: mac";
852   $langs{tip227}  = "Compression level:\n  fast: 0\n  normal: 5\n  best: 8\nSee <man flac>.";
853   $langs{tip228}  = "Put here the additional command line used to encode this files.";
854   $langs{tip229}  = "Used to encode ID3v2.3.0 Tag for MP3 files.";
855   $langs{tip250}  = "If this entry is empty, the original comment tag will be preserved. ";
856   $langs{tip250} .= "The default comment is \"gnormalize - gain=%gain\" with %gain replaced by its value.";
857
858
859   $langs{tip301}  = "If there is more than one artist, use:\nArtist1 & Artist2 & Artist3 & ...";
860   $langs{tip302}  = "Track number";
861   $langs{tip303}  = "Total track number";
862   $langs{tip304}  = "Save changes of tag.";
863   $langs{tip305}  = "Save changes of tag to $home/.cddb directory.";
864
865   $langs{tip401}  = "Try /dev/cdrom, /dev/dvd, /dev/hdc or ...\nSee \"/etc/fstab\" file.";
866   $langs{tip402}  = "Refresh the cdrom drive and erase the playlist.";
867   $langs{tip403}  = "Unselect all tracks.";
868   $langs{tip404}  = "Select all tracks.";
869   $langs{tip405}  = "Add more files to play.";
870   $langs{tip406}  = "Close the gnormalize player config.";
871   $langs{tip407}  = "Choose the interval in milliseconds that the text will be redrawed.";
872   $langs{tip408}  = "Choose the step incremental in pixels.";
873   $langs{tip409}  = "Scrolling text orientation: up/down or left/right.";
874   $langs{tip410}  = "Show the xTunes like skin.\nTo take effect, change the display mode.";
875   $langs{tip411}  = "Always On Top: keep the play mode window above the others windows.\nTo take effect, change the display mode.";
876   $langs{tip429}  = "Play tracks that satisfies established filters can be more convenient or pleasant than the general random approach. ";
877   $langs{tip429} .= "The available filters are 'Play Tracks From Different Albums/Artists/Genres/Years'.";
878   $langs{tip429} .= "\n---***---\n";
879   $langs{tip429} .= "For example, if the filter is set to Albums, then play tracks jumping between different Albums. ";
880   $langs{tip429} .= "In this case, all Albums will be played before repeat some Album. ";
881   $langs{tip429} .= "In others words, until one track has been played from each Album, the same Album will not be chosen twice.";
882   $langs{tip429} .= "\n---***---\n";
883   $langs{tip429} .= "When playing, the filters can be used considering the straight or random order. ";
884
885   $langs{tip501}  = "All tabs, messages, names and tips used by gnormalize.";
886   $langs{tip502}  = "You need to restart the Gnormalize to make the changes.";
887   $langs{tip503}  = "Guidance Language only used to help the translations.\nRead Only.";
888   $langs{tip504}  = "Save the translations and new languages inside the directory: $lang_dir.";
889   $langs{tip505}  = "Improve this translation or use it as base for a new Language.";
890
891
892   $langs{msg001}  = "Supported Files into the directory (recursively):";
893   $langs{msg002}  = "Supported Files into the directory (not recursively):";
894   $langs{msg003}  = " Choose a mp3/mp4/mpc/ogg/ape/flac/wav/cda file to normalize!";
895   $langs{msg004}  = " Dependence not satisfied!";
896   $langs{msg005}  = "If Same Extension, Overwrite file";
897   $langs{msg006}  = "Analyzing with ReplayGain normalization algorithms ...";
898
899   $langs{msg012} = ": read only!";
900   $langs{msg013} = " Select at least one track to rip!";
901   $langs{msg014} = " Success! Rip finished.";
902   $langs{msg015} = " Rip stopped!";
903   $langs{msg016} = " Success!";
904   $langs{msg017} = " Success! File converted.";
905   $langs{msg018} = " Success! File normalized.";
906   $langs{msg019} = "stop ripping";
907   $langs{msg020} = " Get info from ";
908   $langs{msg021} = " Please, wait. cdparanoia is reading the cdrom ...";
909   $langs{msg022} = " Please, select at least one track to play.";
910   $langs{msg023} = "Can't find \"";
911   $langs{msg024} = "\" in executable path.";
912   $langs{msg025} = "The output can't be \"mp3\" format.";
913   $langs{msg026} = "The output can't be \"ogg\" format.";
914   $langs{msg027} = "The output can't be \"mpc\" format.";
915   $langs{msg028} = "The output can't be \"ape\" format.";
916   $langs{msg029} = "The output can't be \"flac\" format.";
917   $langs{msg030} = "The output can't be \"mp4\" format.";
918   $langs{msg031} = "The wav files can't be normalized.";
919   $langs{msg032} = "The input can't be \"ogg\" format.";
920   $langs{msg033} = "The input can't be \"mpc\" format.";
921   $langs{msg034} = "The input can't be \"mp4\" format.";
922   $langs{msg035} = "cdcd is a Command Driven CD player.";
923   $langs{msg036} = "First run \"cdcd\" in command line to make the config file .cdcdrc";
924   $langs{msg037} = "Choose \"n\" for the question:  Are you connected to a network (y/n) [y]?";
925   $langs{msg038} = "Can't use the perl module \"";
926   $langs{msg039} = "Can't play audio CD.";
927   $langs{msg040}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
928   $langs{msg040} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
929   $langs{msg040} .= "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. gnormalize can convert ";
930   $langs{msg040} .= "audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
931   $langs{msg041}  = "This file will not be normalized!";
932   $langs{msg042} = "According to insensibility, not normalize";
933   $langs{msg043} = "Not normalize because adjustment ";
934   $langs{msg044} = " is less than insensibility value.";
935   $langs{msg045} = " not normalize ";
936   $langs{msg046} = "Don't need encode";
937   $langs{msg047} = " already normalized ";
938   $langs{msg048} = "This file is already normalized.";
939   $langs{msg049} = " Stop normalizing!";
940   $langs{msg050} = "Applying adjustment of ";
941   $langs{msg051} = " normalizing: adjustment of ";
942   $langs{msg052} = "Computing levels...";
943   $langs{msg053} = " normalized with adjustment of ";
944   $langs{msg054} = " ripping audio to wav";
945   $langs{msg055} = " Stop ripping!";
946   $langs{msg056} = "copy maked.";
947   $langs{msg057} = " Stop lame decoding!";
948   $langs{msg058} = " Stop lame encoding!";
949   $langs{msg061} = " This informations will be saved on normalized file.";
950   $langs{msg062} = " tag saved.";
951   $langs{msg063} = "Select a mp3/mp4/mpc/ogg/ape/flac/wav file.";
952   $langs{msg064} = "Homepage: $HOMEPAGE\nThis software is available under GNU General Public Licence.";
953   $langs{msg065} = " output: ";
954   $langs{msg066} = "file selected - $langs{all_tab03} can be edited.";
955   $langs{msg067} = " Unable to open cdrom drive ";
956   $langs{msg068} = " Getting information from cddb ...";
957   $langs{msg069} = " none cddb information.";
958   $langs{msg070} = " Press save button on \"$langs{all_tab03}\" tab to save this changes.";
959   $langs{msg071} = " Not connected, using ";
960   $langs{msg072} = " cddb: can't connect to internet!";
961   $langs{msg073} = " Can't rip audio CD! cdparanoia not found.";
962   $langs{msg074} = " Can't rip audio CD! cdda2wav not found.";
963   $langs{msg075} = "Select a mp3, mp4, mpc, ogg, ape, flac, or wav file";
964   $langs{msg077} = "settings";
965   $langs{msg078} = "Selected file";
966   $langs{msg079} = "Recomended minimum version:";
967   $langs{msg080} = " Invalid option!";
968   $langs{msg081} = " Press the \"Refresh the cdrom\" button and type again the changes.";
969   $langs{msg082} = "Not found CDDB_get perl module in your system.\n gnormalize will uses internal CDDB_get (version 2.27).";
970   $langs{msg083} = "Not found MP3::Info perl module in your system.\n gnormalize will uses internal MP3::Info (version 1.20).";
971   $langs{msg084}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
972   $langs{msg084} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
973   $langs{msg085}  = "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. ";
974   $langs{msg085} .= "gnormalize can convert audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
975   $langs{msg086} = "Selected Directory (recursively)";
976   $langs{msg087} = "Select a directory containing mp3, mp4, mpc, ogg, ape, flac, or wav file.";
977   $langs{msg088} = "Selected Directory (not recursively)";
978   $langs{msg089} = "Removing temporary file:";
979   $langs{msg090} = "Can not find no supported file inside of directory!";
980   $langs{msg092} = "remove this music from list";
981   $langs{msg093} = "Author: $AUTHOR\nEmail: $EMAIL\nDate: $DATE ; S�o Paulo - Brasil";
982   $langs{msg094} = "Translation to English: Claudio Fernandes de Souza Rodrigues.";
983   $langs{msg095} = "You must add new language name to translate!";
984   }
985
986   if ( $language eq 'Portugu�s' )
987   {
988   $langs{all_tab01} = "Arquivos";
989   $langs{all_tab02} = "Configura��o";
990   $langs{all_tab03} = "Informa��o";
991   $langs{all_tab04} = "Rip/Play";
992   $langs{all_tab05} = "Tradu��es";
993   $langs{all_tab06} = "Sobre";
994   $langs{all_tab07} = "Op��o1";
995   $langs{all_tab08} = "Op��o2";
996   $langs{all_tab09} = "CDDB";
997   $langs{all_tab10} = "Ripper";
998   $langs{all_tab11} = "Cores";
999   $langs{all_tab12} = "Tocadores";
1000   $langs{all_tab13} = "Colunas";
1001   $langs{all_tab14} = "Visual";
1002
1003   $langs{name001} = "Tipo de Normaliza��o";
1004   $langs{name002} = "Insensibilidade";
1005   $langs{name003} = "Arquivo Selecionado";
1006   $langs{name004} = "Diret�rio de Entrada";
1007   $langs{name005} = "Sa�da: Compress�o Com Perdas";
1008   $langs{name006} = "Sa�da: Compress�o Sem Perdas";
1009   $langs{name106} = "Sa�da: Sem Compress�o";
1010
1011   $langs{name007} = "Arquivo";
1012   $langs{name008} = "Recursivamente";
1013   $langs{name009} = "Remover Wav";
1014   $langs{name010} = "Alterar Propriedades";
1015   $langs{name011} = "Sobrescrever";
1016   $langs{name012} = "Prioridade";
1017   $langs{name013} = "Adicionar Comando";
1018   $langs{name014} = "Arquivo Rip";
1019   $langs{name015} = "Caracteres";
1020   $langs{name016} = "Idioma";
1021   $langs{name017} = "Fonte";
1022   $langs{name018} = "Ler CDDB";
1023   $langs{name019} = "Sobrescrever CDDB";
1024   $langs{name020} = "CDDB Servidor";
1025   $langs{name021} = "Porta";
1026   $langs{name022} = "Transporte";
1027   $langs{name023} = "Desativar Paranoia";
1028   $langs{name024} = "Desativar Extra Paranoia";
1029   $langs{name025} = "Abortar no Caso de Arranh�o";
1030   $langs{name026} = "Reler Indefinidamente";
1031   $langs{name027} = "Paranoia n�o Verificar";
1032   $langs{name028} = "Desativar Paranoia";
1033   $langs{name029} = "Extrator";
1034   $langs{name030} = "Tipo de Codifica��o";
1035   $langs{name031} = "Modo";
1036   $langs{name032} = "Qualidade";
1037   $langs{name033} = "Vari�vel";
1038   $langs{name034} = "T�tulo";
1039   $langs{name035} = "Artista";
1040   $langs{name036} = "�lbum";
1041   $langs{name037} = "Coment�rio";
1042   $langs{name038} = "Ano";
1043   $langs{name039} = "N�mero";
1044   $langs{name040} = "G�nero";
1045   $langs{name041} = "Freq��ncia";
1046   $langs{name042} = "Dura��o";
1047   $langs{name043} = "Canais";
1048   $langs{name044} = "Dura��o";
1049   $langs{name045} = "Faixa";
1050   $langs{name046} = "Artista";
1051   $langs{name047} = "T�tulo";
1052   $langs{name048} = "Tamanho";
1053   $langs{name049} = "Ano";
1054   $langs{name050} = "Arquivo";
1055   $langs{name051} = "Extens�o";
1056   $langs{name052} = "Diret�rio de Sa�da";
1057   $langs{name053} = "�lbum";
1058   $langs{name054} = "Play Count";
1059   $langs{name055} = "Anima��o";
1060   $langs{name056} = "Nome do Arquivo";
1061   $langs{name057} = "Exibir Lembretes";
1062   $langs{name058} = "Idioma Usado como Par�metro";
1063   $langs{name059} = "Atual Tradu��o";
1064   $langs{name060} = "Melhorar a Atual Tradu��o";
1065   $langs{name061} = "Adicionar Novo Idioma";
1066   $langs{name062} = "Adicione Aqui Novo Idioma e Pressione o Bot�o para Salvar.";
1067   $langs{name063} = "Normaliza��o";
1068   $langs{name064} = "CD Player";
1069   $langs{name065} = "Tocar";
1070
1071   $langs{name429} = 'Tocar Arquivos Sem Qualquer Filtro';
1072   $langs{name430} = 'Tocar Arquivos De Diferentes �lbuns';
1073   $langs{name431} = 'Tocar Arquivos De Diferentes Artistas';
1074   $langs{name432} = 'Tocar Arquivos De Diferentes G�neros';
1075   $langs{name433} = 'Tocar Arquivos De Diferentes Anos';
1076
1077   $langs{tip101}  = "Track: usa o ajuste ReplayGain considerando uma �nica Faixa.\nAlbum: ainda n�o implementado!\n";
1078   $langs{tip101} .= "None: nenhuma normaliza��o � realizada.\n";
1079   $langs{tip101} .= "---*---\n";
1080   $langs{tip101} .= "gnormalize utiliza o wavegain para normalizar os arquivos wave.\n";
1081   $langs{tip101} .= "wavegain � um programa que aplica o algor�timo ReplayGain de normaliza��o diretamente aos arquivos wave.";
1082   $langs{tip102}  = "Aplica ganhos adicionais ao audio em decib�is entre -12.0 e +12.0. ";
1083   $langs{tip102} .= "Gain = 0.00dB corresponde ao valor de refer�ncia de 89dB.\n";
1084   $langs{tip102} .= "---*---\n";
1085   $langs{tip102} .= "Id�ias b�sicas do algor�tmo de normaliza��o ReplayGain:\n";
1086   $langs{tip102} .= "1. Calcular a Raiz da M�dia do Quadrados da amplitude RMS(A) a cada intervalo de 50ms;\n";
1087   $langs{tip102} .= "2. Ordenar em ordem crescente os valores RMS(A) e escolher aquele que representa 95% dos volumes percebidos;\n";
1088   $langs{tip102} .= "3. Ajustar a energia do sinal digital ao valor de refer�ncia de 89dB.\n";
1089   $langs{tip102} .= "---*---\n";
1090   $langs{tip102} .= "RMS(A) = sqrt(mean(A^2)) corresponde a Raiz da M�dia dos Quadrados da amplitude sonora A.\n";
1091   $langs{tip102} .= "Gain(A) = 2*10*log(A/A0), tal que A � a amplitude medida e A0 � a amplitude de refer�ncia.";
1092   $langs{tip103}  = "Insensibilidade do normalize. Apenas n�o re-codifica se:\n";
1093   $langs{tip103} .= "1) a extens�o do arquivo de entrada for a mesma da sa�da;\n";
1094   $langs{tip103} .= "2) e o bot�o \"$langs{name010}\" n�o estiver ativo;\n";
1095   $langs{tip103} .= "3) e o ajuste do normalize for menor que o valor de insensibilidade.";
1096   $langs{tip104}  = "Selecione um diret�rio com arquivo suportado.";
1097   $langs{tip105}  = "Escolha o diret�rio onde todos os arquivos ser�o copiados.";
1098   $langs{tip106}  = "Normaliza/converte todos os arquivos suportados dentro do diret�rio (recursivamente ou n�o).";
1099   $langs{tip107}  = "Selecione um diret�rio incluindo subdiret�rios: desce recursivamente dentro do diret�rio preservando a mesma estrutura.";
1100   $langs{tip108}  = "Depois de normalizar, o formato de sa�da ser� mp3. \nPara ouvir arquivos mp3 utilize o mpg123, mpg321 ou madplay.";
1101   $langs{tip109}  = "Depois de normalizar, o formato de sa�da ser� mp4 (tamb�m conhecido como m4a) ou acc. \n";
1102   $langs{tip109} .= "Para ouvir arquivos mp4 utilize o mplayer.";
1103   $langs{tip110}  = "Depois de normalizar, o formato de sa�da ser� mpc (tamb�m conhecido como mpp ou mp+). \n";
1104   $langs{tip110} .= "Para ouvir arquivos mpc utilize o comando mppdec ou o mplayer.";
1105   $langs{tip111}  = "Depois de normalizar, o formato de sa�da ser� ogg. \nPara ouvir arquivos ogg utilize o ogg123.";
1106   $langs{tip112}  = "Depois de normalizar, o formato de sa�da ser� ape. \nPara ouvir arquivos ape use o comando:\n   mac \"song.ape\"  - -d | play -t wav - ";
1107   $langs{tip113}  = "Depois de normalizar, o formato de sa�da ser� flac. \nPara ouvir arquivos flac utilize o flac123 ou o mplayer.\nVers�o do flac exigida >= \"1.1.1\".";
1108   $langs{tip114}  = "Esta op��o � conveniente para gravar um cd de audio.\nWav � um formato de audio sem compress�o.";
1109   $langs{tip115}  = "NOTE:\n";
1110   $langs{tip115} .= "Neste procedimento, conhecido por 'Lossy Compression', dados podem ser perdidos durante a compress�o. ";
1111   $langs{tip115} .= "O grau de perda varia de acordo com os ajustes adotados e dependendo destes ajustes a perda � m�nima e n�o percept�vel ";
1112   $langs{tip115} .= "ao ouvido humano.\nExemplos de audio com Lossy Compression: Mp3, Mp4, Mpc e Ogg.";
1113   $langs{tip116}  = "NOTE:\n";
1114   $langs{tip116} .= "Este procedimento � conhecido por 'Lossless Compression' ou 'Livre de Perdas' em que toda informa��o � preservada ";
1115   $langs{tip116} .= "durante a compress�o e todos os dados podem ser recuperados de forma id�ntica aos originais.\n";
1116   $langs{tip116} .= "Exemplos de audio com Lossless Compression: Ape e Flac.";
1117   $langs{tip117}  = "Exibir a barra de progresso.";
1118   $langs{tip118}  = "Omitir a barra de progresso.";
1119   $langs{tip119}  = "Exibir as linhas de comando e outras informa��es.";
1120   $langs{tip120}  = "Omitir as linhas de comando e outras informa��es.";
1121   $langs{tip121}  = "Sair.";
1122   $langs{tip122}  = "Apagar todas as mensagens.";
1123
1124   $langs{tip201}  = "Depois do processo de normaliza��o, os arquivos tempor�rios wav ser�o removidos/apagados.";
1125   $langs{tip202}  = "Depois de normalizar os arquivos, voc� pode alterar suas propriedades determinando o tipo de codifica��o, ";
1126   $langs{tip202} .= "o bitrate de todos os arquivos de sa�da, etc. Caso contr�rio, o bitrate de cada arquivo de entrada e ";
1127   $langs{tip202} .= "outras propriedades ser�o conservados na medida do poss�vel.";
1128   $langs{tip203}  = "O arquivo normalizado ser� salvo no mesmo diret�rio da entrada e ocorrer� sobrescri��o (salvar por cima) se existir outro arquivo de mesmo nome.";
1129   $langs{tip204}  = "Exibir sugest�es e informa��es.";
1130   $langs{tip205}  = "Mostrar a anima��o do Tux e de outros ping�ins.";
1131   $langs{tip206}  = "Modificar o gerenciamento de prioridade na execu��o dos comandos. Seu valor varia de 0 (maior prioridade) at� 19 (menor prioridade).";
1132   $langs{tip207}  = "Formato para o nome final do arquivo, usado apenas para ripar/extrair arquivos do CD de audio:\n";
1133   $langs{tip207} .= "   %a - artista\n";
1134   $langs{tip207} .= "   %b - album\n";
1135   $langs{tip207} .= "   %t  - t�tulo da m�sica\n";
1136   $langs{tip207} .= "   %n - n�mero da faixa\n";
1137   $langs{tip207} .= "   %y - ano\n";
1138   $langs{tip207} .= "   %g - g�nero";
1139   $langs{tip208}  = "Fonte aconselhada: Sans 10.";
1140   $langs{tip209}  = "Carregar informa��es para o CD de audio em seu drive a partir do servidor CDDB \"$CDDB_server\" ou do arquivo copiado em $home/.cddd/.";
1141   $langs{tip210}  = "Sempre buscar informa��es do servidor CDDB e sobrescrever o arquivo existente em $home/.cddd/ .";
1142   $langs{tip211}  = "Alguns servidores:\nfreedb.freedb.org:8880\nus.freedb.org:8880\ncddb.com\nus.cddb.com:8880\nsunsite.unc.edu:8880";
1143   $langs{tip212}  = "Escolha o n�mero da porta.";
1144   $langs{tip213}  = "Desativar toda a verifica��o e corre��o de erros na leitura de dados. Consequentemente a extra��o � mais r�pida. ";
1145   $langs{tip213} .= "Esta op��o tamb�m implica em (op��o -Y) ativa. Veja cdparanoia (op��o -Z).";
1146   $langs{tip214}  = "N�o aceita nenhuma omiss�o na leitura dos dados; ler indefinidamente se necess�rio. Veja cdparanoia (op��o -z).";
1147   $langs{tip215}  = "Desativa a verifica��o interna dos dados; apenas cobre e verifica a regi�o das fronteiras. N�o � recomendado. Veja cdparanoia (op��o -Y).";
1148   $langs{tip216}  = "Se a leitura pula/salta devido a dados imperfeitos, arranh�es, etc., abandonar a leitura desta faixa do CD. Veja cdparanoia (op��o -X).";
1149   $langs{tip217}  = "Usar, para a leitura do CD, a biblioteca do paranoia ao inv�s das rotinas do cdda2wav. Veja cdda2wav (op��o -paranoia).";
1150   $langs{tip218}  = "Desativar o modo paranoia. Mas, paranoia ainda assim ser� usado. Veja cdda2wav (op��o -paraopts=help).";
1151   $langs{tip219}  = "Desativa a verifica��o, mas habilita a leitura sobreposta da regi�o das fronteiras.";
1152   $langs{tip220}  = "Escolha o valor do bitrate m�dio. Ajusta o codificador/encoder a este valor o ma�s pr�ximo poss�vel (in Kb/s). ";
1153   $langs{tip220} .= "Bitrate � a taxa de bits utilizada no arquivo por unidade de tempo.";
1154   $langs{tip221}  = "Ajusta a qualidade do mp3; 9 � a pior qualidade, 0 � a melhor qualidade com mais Kb/s usado. Veja: man lame (op��o: -V).";
1155   $langs{tip222}  = "Ajusta a qualidade do ogg; 10 � a melhor qualidade com mais Kb/s usado, -1 � a pior qualidade. Veja: man oggenc.";
1156   $langs{tip223}  = "Ajusta a qualidade do codificador lame para os arquivos mp3: 0 � a melhor qualidade com a m�xima itera��o no processo algor�tmo; ";
1157   $langs{tip223} .= "9 � a pior qualidade, contudo muito mais r�pido. Veja: man lame (op��o: -q).";
1158   $langs{tip224}  = "Ajusta a qualidade do vari�vel bitrate (VBR) em porcentagem para o formato mp4; 500 � a melhor ";
1159   $langs{tip224} .= "qualidade com mais Kb/s usado; 10 � a pior qualidade. Veja: <faac --help>.";
1160   $langs{tip225}  = "Ajusta a qualidade de codifica��o para os arquivos mpc usando um conjunto de rotinas (profiles); ";
1161   $langs{tip225} .= "8 (braindead) � a melhor qualidade com mais Kb/s usado, 2 (telephone) � a pior qualidade. Veja: <mppenc --longhelp>.\n";
1162   $langs{tip225} .= " q=2 --telephone - pior qualidade,           (32... 48 kbps)\n";
1163   $langs{tip225} .= " q=3 --thumb     - baixa qualidade/internet, (58... 86 kbps)\n";
1164   $langs{tip225} .= " q=4 --radio     - qualidade m�dia (MP3),    (112...152 kbps)\n";
1165   $langs{tip225} .= " q=5 --standard  - qualidade alta (padr�o),  (142...184 kbps)\n";
1166   $langs{tip225} .= " q=6 --xtreme    - qualidade muito alta, (168...212 kbps)\n";
1167   $langs{tip225} .= " q=7 --insane    - qualidade muito alta, (232...268 kbps)\n";
1168   $langs{tip225} .= " q=8 --braindead - qualidade alt�ssima,         (232...278 kbps)";
1169   $langs{tip226}  = "N�vel de Compress�o:\n  fast (r�pida): 1000\n  normal: 2000\n  high (alta): 3000\n  extra high: 4000\n  insane: 5000\n";
1170   $langs{tip226} .= "\nMaior n�vel de compress�o resulta \nem arquivos menores.\nVeja o comando: mac";
1171   $langs{tip227}  = "N�vel de Compress�o:\n  fast (r�pida): 0\n  normal: 5\n  best (m�xima): 8\nVeja <man flac>.";
1172   $langs{tip228}  = "Coloque aqui linha de comando adicional usada para codificar estes arquivos.";
1173   $langs{tip229}  = "Usado para codificar o ID3v2.3.0 Tag em arquivos MP3.";
1174   $langs{tip250}  = "Se esta entrada estiver vazia, o coment�rio original do Tag ser� preservado. ";
1175   $langs{tip250} .= "O coment�rio padr�o � \"gnormalize - gain=%gain\" com %gain substitu�do pelo seu valor.";
1176
1177   $langs{tip301}  = "Se houver mais de um artista, use:\nArtista1 & Artista2 & Artista3 & ...";
1178   $langs{tip302}  = "N�mero da m�sica.";
1179   $langs{tip303}  = "N�mero total de m�sicas.";
1180   $langs{tip304}  = "Salvar altera��es do tag.";
1181   $langs{tip305}  = "Salvar altera��es do tag no diret�rio $home/.cddb .";
1182
1183   $langs{tip401}  = "Tente /dev/cdrom, /dev/dvd, /dev/hdc ou ...\nVeja o arquivo \"/etc/fstab\".";
1184   $langs{tip402}  = "Atualiza o drive do cdrom e apaga a lista de m�sicas.";
1185   $langs{tip403}  = "Selecionar nenhuma m�sica.";
1186   $langs{tip404}  = "Selecionar todas as m�sicas.";
1187   $langs{tip405}  = "Adicionar mais arquivos para tocar.";
1188   $langs{tip406}  = "Fechar a configura��o do gnormalize player.";
1189   $langs{tip407}  = "Escolha o intervalo em milisegundos que o texto ser� redesenhado.";
1190   $langs{tip408}  = "Escolha o incremento no deslocamento do texto em pixels.";
1191   $langs{tip409}  = "Orienta��o no deslocamento do texto: cima/baixo ou esquerda/direita.";
1192   $langs{tip410}  = "Exibir um visual semelhante ao xTunes.\nPara fazer efeito, altere o modo de exibi��o.";
1193   $langs{tip411}  = "Exibir Sempre: for�ar que este modo de exibi��o fique acima de outras janelas.\nPara fazer efeito, altere o modo de exibi��o.";
1194   $langs{tip429}  = "Tocar arquivos que satisfazem determinados filtros pode ser mais coveniente ou agrad�vel que o m�todo aleat�rio geral. ";
1195   $langs{tip429} .= "Os filtros dispon�veis s�o 'Tocar Arquivos de Diferentes �lbuns/Artistas/G�neros/Anos'.";
1196   $langs{tip429} .= "\n---***---\n";
1197   $langs{tip429} .= "Por exemplo, se o filtro escolhido for �lbuns, ent�o ser�o tocados arquivos entre os diferentes �lbuns. ";
1198   $langs{tip429} .= "Neste caso, todos os �lbuns ser�o tocados antes de repetir algum outro �lbum. ";
1199   $langs{tip429} .= "Em outras palavras, at� que um arquivo tenha sido tocado de cada �lbum, um mesmo �lbum n�o ser� escolhido duas vezes.";
1200   $langs{tip429} .= "\n---***---\n";
1201   $langs{tip429} .= "Ao tocar, os filtros podem ser usados considerando a ordem direta ou aleat�ria. ";
1202
1203   $langs{tip501}  = "Todas as abas, mensagens, nomes e lembretes usados pelo gnormalize.";
1204   $langs{tip502}  = "Voc� precisa reiniciar o Gnormalize para efetuar as mudan�as.";
1205   $langs{tip503}  = "Idioma usado apenas como par�metro para auxiliar na tradu��o.\nApenas para leitura.";
1206   $langs{tip504}  = "Salvar as altera��es e novas tradu��es dentro do diret�rio: $lang_dir.";
1207   $langs{tip505}  = "Aperfei�oe esta tradu��o ou use-a como base para um novo Idioma.";
1208
1209
1210   $langs{msg001}  = "Arquivos suportados dentro do diret�rio (recursivamente):";
1211   $langs{msg002}  = "Arquivos suportados dentro do diret�rio (n�o recursivamente):";
1212   $langs{msg003}  = " Escolha um arquivo mp3/mp4/mpc/ogg/ape/flac/wav/cda para normalizar!";
1213   $langs{msg004}  = " Depend�ncia n�o satisfeita!";
1214   $langs{msg005}  = "Se Mesma Extens�o, Sobrescrever Arquivo";
1215   $langs{msg006}  = "Analisando com o algoritmo ReplayGain de normaliza��o ...";
1216
1217   $langs{msg012} = ": apenas para leitura!";
1218   $langs{msg013} = " Selecione ao menos uma m�sica para ripar/extrair!";
1219   $langs{msg014} = " Sucesso! Extra��o finalizada.";
1220   $langs{msg015} = " Extra��o interrompida!";
1221   $langs{msg016} = " Sucesso!";
1222   $langs{msg017} = " Sucesso! Arquivo convertido.";
1223   $langs{msg018} = " Sucesso! Arquivo normalizado.";
1224   $langs{msg019} = "Interromper a extra��o";
1225   $langs{msg020} = " Informa��o obtida de ";
1226   $langs{msg021} = " Favor esperar. cdparanoia est� lendo o cdrom ...";
1227   $langs{msg022} = " Favor, selecione pelo menos uma m�sica para tocar.";
1228
1229   $langs{msg023} = "N�o encontrado \"";
1230   $langs{msg024} = "\" nos diret�rios execut�veis.";
1231   $langs{msg025} = "O formato de sa�da n�o pode ser \"mp3\".";
1232   $langs{msg026} = "O formato de sa�da n�o pode ser \"ogg\".";
1233   $langs{msg027} = "O formato de sa�da n�o pode ser \"mpc\".";
1234   $langs{msg028} = "O formato de sa�da n�o pode ser \"ape\".";
1235   $langs{msg029} = "O formato de sa�da n�o pode ser \"flac\".";
1236   $langs{msg030} = "O formato de sa�da n�o pode ser \"mp4\".";
1237   $langs{msg031} = "O arquivo wav n�o pode ser normalizado.";
1238   $langs{msg032} = "O formato de entrada n�o poder ser \"ogg\".";
1239   $langs{msg033} = "O formato de entrada n�o poder ser \"mpc\".";
1240   $langs{msg034} = "O formato de entrada n�o poder ser \"mp4\".";
1241   $langs{msg035} = "cdcd � um tocador de CD em linha de comando.";
1242   $langs{msg036} = "Primeiro execute \"cdcd\" em linha de comando para gerar o arquivo de configura��o .cdcdrc";
1243   $langs{msg037} = "Escolha \"n\" para a quest�o:  Are you connected to a network (y/n) [y]?";
1244   $langs{msg038} = "N�o pode ser usado o m�dulo perl \"";
1245   $langs{msg039} = "CD de audio n�o poder ser tocado.";
1246   $langs{msg040}  = "gnormalize � um conversor e extractor de CDs de Audio com aplica��o do algor�timo ReplayGain de normaliza��o, um tocador de audio e um ";
1247   $langs{msg040} .= "editor de metadata (tag). gnormalize utiliza o 'wavegain' para normalizar arquivos WAV com a acur�cia do c�digo gain_analysis.c do ReplayGain. ";
1248   $langs{msg040} .= "gnormalize decodifica os arquivos para o formato WAV, normaliza os arquivos WAV e re-codifica-os. gnormalize suporta convers�o ";
1249   $langs{msg040} .= "entre os formatos de audio MP3, MP4, MPC, OGG, APE, FLAC e WAV.";
1250   $langs{msg041}  = "Este arquivo n�o ser� normalizado!";
1251   $langs{msg042} = "De acordo com a insensibilidade, n�o normalizar";
1252   $langs{msg043} = "N�o normalizar pois o ajuste ";
1253   $langs{msg044} = " � menor que o valor da insensibilidade.";
1254   $langs{msg045} = " n�o normalizar ";
1255   $langs{msg046} = "N�o necessita codificar/encodar";
1256   $langs{msg047} = " j� normalizado ";
1257   $langs{msg048} = "Este arquivo j� est� normalizado.";
1258   $langs{msg049} = " Interromper a normaliza��o!";
1259   $langs{msg050} = "Aplicando ajuste de ";
1260   $langs{msg051} = " normalizando: ajuste de ";
1261   $langs{msg052} = "Calculando o ajuste ...";
1262   $langs{msg053} = " normalizado com ajuste de ";
1263   $langs{msg054} = " extraindo audio para wav";
1264   $langs{msg055} = " Interrompendo a extra��o!";
1265   $langs{msg056} = "c�pia realizada.";
1266   $langs{msg057} = " Parar decodifica��o lame!";
1267   $langs{msg058} = " Parar codifica��o lame!";
1268   $langs{msg061} = " Estas informa��es ser�o salvas no arquivo normalizado.";
1269   $langs{msg062} = " tag alterado.";
1270   $langs{msg063} = "Selecione um arquivo mp3/mp4/mpc/ogg/ape/flac/wav .";
1271   $langs{msg064} = "Homepage: $HOMEPAGE\nEste software est� dispon�vel sob a GNU General Public Licence.";
1272   $langs{msg065} = " sa�da: ";
1273   $langs{msg066} = "selecionado - $langs{all_tab03} pode ser editado.";
1274   $langs{msg067} = " N�o foi poss�vel ler o cdrom no drive ";
1275   $langs{msg068} = " Obtendo informa��o do cddb ...";
1276   $langs{msg069} = " nenhuma informa��o do cddb.";
1277   $langs{msg070} = " Pressione o bot�o Save (Salvar) em \"$langs{all_tab03}\" para salvar as altera��es.";
1278   $langs{msg071} = " N�o conectado, usando ";
1279   $langs{msg072} = " cddb: falha ao conectar-se � internet!";
1280   $langs{msg073} = " N�o pode extrair CD de audio! cdparanoia n�o encontrado.";
1281   $langs{msg074} = " N�o pode extrair CD de audio! cdda2wav n�o encontrado.";
1282   $langs{msg075} = "Selecione um arquivo mp3, mp4, mpc, ogg, ape, flac, ou wav.";
1283   $langs{msg077} = "ajustes";
1284   $langs{msg078} = "Arquivo selecionado";
1285   $langs{msg079} = "Vers�o m�nima recomendada:";
1286   $langs{msg080} = " Op��o Inv�lida!";
1287   $langs{msg081} = " Pressione o bot�o \"Atualizar o cdrom\" e digite novamente as altera��es.";
1288   $langs{msg082} = "O perl m�dulo CDDB_get n�o foi encontrado no seu sistema.\n gnormalize ir� usar o CDDB_get (vers�o 2.27) interno.";
1289   $langs{msg083} = "O perl m�dulo MP3::Info n�o foi encontrado no seu sistema.\n gnormalize ir� usar o MP3::Info (vers�o 1.20) interno.";
1290   $langs{msg084}  = "gnormalize � um conversor e extractor de CDs de Audio com aplica��o do algor�timo ReplayGain de normaliza��o, um tocador de audio e um ";
1291   $langs{msg084} .= "editor de metadata (tag). gnormalize utiliza o 'wavegain' para normalizar arquivos WAV com a acur�cia do c�digo gain_analysis.c do ReplayGain. ";
1292   $langs{msg085}  = "gnormalize decodifica os arquivos para o formato WAV, normaliza os arquivos WAV e re-codifica-os. gnormalize suporta convers�o ";
1293   $langs{msg085} .= "entre os formatos de audio MP3, MP4, MPC, OGG, APE, FLAC e WAV.";
1294   $langs{msg086} = "Diret�rio selecionado (recursivamente)";
1295   $langs{msg087} = "Selecione um diret�rio contendo arquivo mp3, mp4, mpc, ogg, ape, flac, ou wav.";
1296   $langs{msg088} = "Diret�rio selecionado (n�o recursivamente):";
1297   $langs{msg089} = "Removendo arquivo tempor�rio";
1298   $langs{msg090} = "Nenhum arquivo suportado foi encontrado dentro do diret�rio!";
1299   $langs{msg092} = "remove esta m�sica da lista";
1300   $langs{msg093} = "Autor: $AUTHOR\nEmail: $EMAIL\nData: $DATE ; S�o Paulo - Brasil";
1301   $langs{msg094} = "Tradu��o para o Portugu�s: Claudio Fernandes de Souza Rodrigues.";
1302   $langs{msg095} = "Voc� deve adicionar o nome do novo Idioma para traduzir.";
1303   }
1304
1305   if ( $language eq 'Fran�ais' )
1306   {
1307   $langs{all_tab01} = "Fichiers";
1308   $langs{all_tab02} = "Configuration";
1309   $langs{all_tab03} = "Information";
1310   $langs{all_tab04} = "Rip/Play";
1311   $langs{all_tab05} = "Translations";
1312   $langs{all_tab06} = "De";
1313   $langs{all_tab07} = "Option1";
1314   $langs{all_tab08} = "Option2";
1315   $langs{all_tab09} = "CDDB";
1316   $langs{all_tab10} = "Ripper";
1317   $langs{all_tab11} = "Colors";
1318   $langs{all_tab12} = "Players";
1319   $langs{all_tab13} = "Columns";
1320   $langs{all_tab14} = "Skins";
1321
1322   $langs{name001} = "Type de Normalisation";
1323   $langs{name002} = "Insensibilit�";
1324   $langs{name003} = "Fichier choisi";
1325   $langs{name004} = "R�pertoire de d�part";
1326   $langs{name005} = "Output: Lossy Compression";
1327   $langs{name006} = "Output: Lossless Compression";
1328   $langs{name106} = "Output: Uncompressed";
1329
1330   $langs{name007} = "Fichier";
1331   $langs{name008} = "R�cursivement";
1332   $langs{name009} = "Effacer les .wav";
1333   $langs{name010} = "Changer les Propri�t�s";
1334   $langs{name011} = "Remplacer";
1335   $langs{name012} = "Propri�t�";
1336   $langs{name013} = "Ajoute la Commande";
1337   $langs{name014} = "Nom du Fichier Ripp�";
1338   $langs{name015} = "Caract�re";
1339   $langs{name016} = "Idiom";
1340   $langs{name017} = "Police";
1341   $langs{name018} = "Lire CDDB";
1342   $langs{name019} = "Remplacer CDDB";
1343   $langs{name020} = "Serveur CDDB";
1344   $langs{name021} = "Port";
1345   $langs{name022} = "Transport";
1346   $langs{name023} = "D�sactiver Paranoia";
1347   $langs{name024} = "D�sactiver Extra Paranoia";
1348   $langs{name025} = "Annuler si Saut�";
1349   $langs{name026} = "Never Skip Repair";
1350   $langs{name027} = "Paranoia No-Verify";
1351   $langs{name028} = "Paranoia D�sativ�e";
1352   $langs{name029} = "Ripper";
1353   $langs{name030} = "Type d\'encodage";
1354   $langs{name031} = "Mode";
1355   $langs{name032} = "Qualit� d\'encodage";
1356   $langs{name033} = "Variable";
1357   $langs{name034} = "Titre";
1358   $langs{name035} = "Artiste";
1359   $langs{name036} = "Album";
1360   $langs{name037} = "Commentaire";
1361   $langs{name038} = "Ann�e";
1362   $langs{name039} = "Num. Piste";
1363   $langs{name040} = "Genre";
1364   $langs{name041} = "Frequency";
1365   $langs{name042} = "Temps";
1366   $langs{name043} = "Canaux";
1367   $langs{name044} = "Length";
1368   $langs{name045} = "Piste";
1369   $langs{name046} = "Artiste";
1370   $langs{name047} = "Titre";
1371   $langs{name048} = "Taille";
1372   $langs{name049} = "Ann�e";
1373   $langs{name050} = "Chemin";
1374   $langs{name051} = "Extension";
1375   $langs{name052} = "R�pertoire de Sortie";
1376   $langs{name053} = "Album";
1377   $langs{name054} = "Play Count";
1378   $langs{name055} = "Animation";
1379   $langs{name056} = "Nom de Fichier";
1380   $langs{name057} = "Show Tooltips";
1381   $langs{name058} = "Guidance Language";
1382   $langs{name059} = "Current Translation";
1383   $langs{name060} = "Improve The Currente Language Translation";
1384   $langs{name061} = "Add New Language";
1385   $langs{name062} = "Add new language here and then press save buton.";
1386   $langs{name063} = "Normaliser";
1387   $langs{name064} = "CD Player";
1388   $langs{name065} = "Play";
1389
1390   $langs{name429} = 'Play Tracks Without Any Filters';
1391   $langs{name430} = 'Play Tracks From Different Albums';
1392   $langs{name431} = 'Play Tracks From Different Artists';
1393   $langs{name432} = 'Play Tracks From Different Genres';
1394   $langs{name433} = 'Play Tracks From Different Years';
1395
1396   $langs{tip101}  = "Track: use ReplayGain Single Track gain setting.\nAlbum: don't implemented yet!\n";
1397   $langs{tip101} .= "None: no normalization is done.\n";
1398   $langs{tip101} .= "---*---\n";
1399   $langs{tip101} .= "gnormalize uses the wavegain to normalize wave files.\n";
1400   $langs{tip101} .= "wavegain is a program that applies ReplayGain normalization algorithms directly to wave files.";
1401   $langs{tip102}  = "Apply additional Manual Gain adjustment in decibels between -12.0 and +12.0. ";
1402   $langs{tip102} .= "Gain = 0.00dB correspond to 89dB standard or reference value.\n";
1403   $langs{tip102} .= "---*---\n";
1404   $langs{tip102} .= "Basic ideas of ReplayGain normalization algorithms:\n";
1405   $langs{tip102} .= "1. Calculate the Root Mean Square (RMS) of the waveform every 50ms;\n";
1406   $langs{tip102} .= "2. Sort the RMS values into numerical order, and choose the value 95% up the list to represent the overall perceived loudness;\n";
1407   $langs{tip102} .= "3. Calibrate the energy of a digital signal to 89dB standard value and apply the gain adjustment.\n";
1408   $langs{tip102} .= "---*---\n";
1409   $langs{tip102} .= "RMS(A) = sqrt(mean(A^2)) is the Root Mean Square of sound amplitude A.\n";
1410   $langs{tip102} .= "Gain(A) = 2*10*log(A/A0), such that A is the measured amplitude and A0 is the reference amplitude.";
1411   $langs{tip103}  = "Insensibilit� de normalisation. Ne pas re-encoder si:\n";
1412   $langs{tip103} .= "1) l\'extension du fichier de sortie est identique � l\'entr�e;\n";
1413   $langs{tip103} .= "2) et que le bouton \"$langs{name010}\" n\'est pas actif;\n";
1414   $langs{tip103} .= "3) et l\'ajustement de normalisation est plus petit que la valeur d\'insensibilit�.";
1415   $langs{tip104}  = "S�lectionner un r�pertoire avec fichier support�.";
1416   $langs{tip105}  = "Choisir le r�pertoire o� les fichiers seront sauv�s.";
1417   $langs{tip106}  = "Normaliser/Convertir tous les fichiers du r�pertoire (r�cursivement ou pas).";
1418   $langs{tip107}  = "Inclure les sous-r�pertoires: la r�cursivit� pr�serve l'arborescence.";
1419   $langs{tip108}  = "Apr�s la normalisation, le format de sortie sera mp3. Ouvrez le avec mpg123, mpg321 ou le madplay.";
1420   $langs{tip109}  = "Apr�s la normalisation, le format de sortie sera mp4 (aussi m4a) ou aac. Ouvrez le avec le mplayer.";
1421   $langs{tip110}  = "Apr�s la normalisation, le format de sortie sera mpc (aussi mpp or mp+). Ouvrez le avec mppdec ou le mplayer.";
1422   $langs{tip111}  = "Apr�s la normalisation, le format de sortie sera ogg. Ouvrez le avec ogg123.";
1423   $langs{tip112}  = "Apr�s la normalisation, le format de sortie sera ape. Ouvrez le avec la commande:\n   mac \"song.ape\"  - -d | play -t wav - ";
1424   $langs{tip113}  = "Apr�s la normalisation, le format de sortie sera flac. Ouvrez le avec flac123 ou le mplayer.\nRequiert une version flac >= \"1.1.1\".";
1425   $langs{tip114}  = "Ce qui convient pour faire un CD Audio.\nWav est un format audio non comprim�.";
1426   $langs{tip115}  = "NOTE:\n";
1427   $langs{tip115} .= "Lossy Compression, certaines informations seront perdues, le format originel ne pourra �tre retrouv� ";
1428   $langs{tip115} .= "et sera sensiblement diff�rent.\nExemples de compression audio avec perte: Mp3, Mp4, Mpc et Ogg.";
1429   $langs{tip116}  = "NOTE:\n";
1430   $langs{tip116} .= "La compression devrait �tre Lossless Compression, toutes les informations d\'origine seront pr�serv�es, ";
1431   $langs{tip116} .= "et les donn�es peuvent �tre remises en l'�tat originel.\nExemples de compression audio sans perte: Ape et Flac.";
1432   $langs{tip117}  = "Montrer la progression.";
1433   $langs{tip118}  = "Cacher laprogression.";
1434   $langs{tip119}  = "Voir la ligne de commande et autres infos.";
1435   $langs{tip120}  = "Cacher la ligne de commande et autres infos.";
1436   $langs{tip121}  = "Quitter.";
1437   $langs{tip122}  = "Effacer tous les messages.";
1438
1439   $langs{tip201}  = "Apr�s le processus de normalisation, les fichiers wav seront effac�s.";
1440   $langs{tip202}  = "Apr�s normalisation, vous pouvez changer les propri�t�s d\'encodage comme le type d\'encodage, le bitratede tous ";
1441   $langs{tip202} .= "les fichiers de sortie, etc. Autrement, les propri�t�s de d�part seront conserv�es.";
1442   $langs{tip203}  = "Le fichier normalis� sera sauv� dans le m�me r�pertoire que celui de d�part. S'il existe d�j�, le remplacer.";
1443   $langs{tip204}  = "Show the hints and informations.";
1444   $langs{tip205}  = "Montrer les animations Tux et compagnie.";
1445   $langs{tip206}  = "Modifier la priorit�: 0 (la plus forte) to 19 (la plus faible)";
1446   $langs{tip207}  = "Le formatage des noms de fichiers en sortie uniquement pour les rip de CD audio:\n";
1447   $langs{tip207} .= "   %a - artiste\n";
1448   $langs{tip207} .= "   %b - album\n";
1449   $langs{tip207} .= "   %t - titre chanson\n";
1450   $langs{tip207} .= "   %n - num�ro piste\n";
1451   $langs{tip207} .= "   %y - ann�e\n";
1452   $langs{tip207} .= "   %g - genre";
1453   $langs{tip208}  = "Police: Sans 10.";
1454   $langs{tip209}  = "Lire les infos CDDB pour les CD Audio depuis le serveur CDDB \"$CDDB_server\" ou depuis un fichier sauv� dans $home/.cddd/.";
1455   $langs{tip210}  = "Toujours r�cup�rer les infos CDDB depuis le serveur et remplacer les infos CDDB sauv�s dans $home/.cddd/";
1456   $langs{tip211}  = "Des serveurs:\nfreedb.freedb.org:8880\nus.freedb.org:8880\ncddb.com\nus.cddb.com:8880\nsunsite.unc.edu:8880";
1457   $langs{tip212}  = "Choisir le num�ro de port.";
1458   $langs{tip213}  = "D�sactive toute v�rification et correction de donn�es. Le rip sera plus rapide. ";
1459   $langs{tip213} .= "Cette option implique que -Y est actif. Voir cdparanoia (option -Z).";
1460   $langs{tip214}  = "Ne pas accepter de saut d\'erreurs; toujours recommencer au besoin. Voir cdparanoia (option -z).";
1461   $langs{tip215}  = "D�sactiver la v�rification interne; only overlap checking at read boundaries is performed. Non recommand�. Voir cdparanoia (option -Y).";
1462   $langs{tip216}  = "Si les sauts d\'erreur r�sultants de donn�es endommag�es ou autres, annuler la lecture de cette piste. Voir cdparanoia (option -X).";
1463   $langs{tip217}  = "Utiliser la librairie paranoia au lieu des routines cdda2wav pour lire. Voir cdda2wav (option -paranoia).";
1464   $langs{tip218}  = "D�sactiver le mode paranoia. Paranoia est encore utilis�. Voir cdda2wav (option -paraopts=help).";
1465   $langs{tip219}  = "Passer � pas de v�rification et saut d\'erreur.";
1466   $langs{tip220}  = "Choisir un bitrate moyen. L\'encodage sera le plus proche possible de cette valeur (en Kb/s).";
1467   $langs{tip221}  = "Ajuster la qualit� mp3; 9 est la pire, 0 est la meilleure (en Kb/s). Voir: man lame (option: -V).";
1468   $langs{tip222}  = "Ajuster la qualit� ogg; 10 est la meilleure (en Kb/s), -1 est la pire. Voir: man oggenc.";
1469   $langs{tip223}  = "Ajuster la qualit� de l\'encodage lame pour les mp3: 0 est la meilleure avec le maximum d\' ";
1470   $langs{tip223} .= "algorithmes; 9 est la pire et la plus rapide. Voir: man lame (option: -q).";
1471   $langs{tip224}  = "Choisir la qualit� du quantizer (VBR) en pourcentage pour le mp4; 500 est la meilleure ";
1472   $langs{tip224} .= "(en Kb/s utilis�s); 10 est la pire. Voir: <faac --help>.";
1473   $langs{tip225}  =  "Ajuster la qualit� de l\'encodage des mpc en utilisant les profiles; 8 (braindead) est la meilleure (en Kb/s),";
1474   $langs{tip225} .= " 2 (t�l�phone) la pire qualit�. Voir: mppenc --longhelp\n";
1475   $langs{tip225} .= " q=2 --t�l�phone - plus basse qualit�,       (32... 48 kbps)\n";
1476   $langs{tip225} .= " q=3 --thumb     - basse qualit�/internet, (58... 86 kbps)\n";
1477   $langs{tip225} .= " q=4 --radio     - qualit� moyenne (MP3), (112...152 kbps)\n";
1478   $langs{tip225} .= " q=5 --standard  - haute qualit� (dflt),  (142...184 kbps)\n";
1479   $langs{tip225} .= " q=6 --xtreme    - extreme haute qualit�, (168...212 kbps)\n";
1480   $langs{tip225} .= " q=7 --insane    - extreme haute qualit�, (232...268 kbps)\n";
1481   $langs{tip225} .= " q=8 --braindead - extreme haute qualit�, (232...278 kbps)";
1482   $langs{tip226}  = "Niveau de compression:\n  rapide: 1000\n  normal: 2000\n  �lev�: 3000\n  tr�s �lev�: 4000\n  le top: 5000\n";
1483   $langs{tip226} .= "\nPlus la compression est forte \nplus les fichiers seront petits.\nVoir l'aide: mac";
1484   $langs{tip227}  = "Niveau de compression:\n  rapide: 0\n  normal: 5\n  meilleur: 8\nVoir <man flac>.";
1485   $langs{tip228}  = "Ajouter ici votre ligne de commande additionnelle pour encoder les fichiers.";
1486   $langs{tip229}  = "Used to encode ID3v2.3.0 Tag for MP3 files.";
1487   $langs{tip250}  = "If this entry is empty, the original comment tag will be preserved. ";
1488   $langs{tip250} .= "The default comment is \"gnormalize - gain=%gain\" with %gain replaced by its value.";
1489
1490   $langs{tip301}  = "S\'il y a plus d'un artiste (compilation), utiliser:\nArtiste1 & Artiste2 & Artiste3 & ...";
1491   $langs{tip302}  = "Num�ro piste";
1492   $langs{tip303}  = "Nombre total pistes";
1493   $langs{tip304}  = "Sauver les changements de tag.";
1494   $langs{tip305}  = "Sauver les changements de tag vers $home/.cddb.";
1495
1496   $langs{tip401}  = "Essayer /dev/cdrom, /dev/dvd, /dev/hdc ou ...\nVoir le fichier \"/etc/fstab\".";
1497   $langs{tip402}  = "Rafra�chir les cdrom et effacer la liste de lecture.";
1498   $langs{tip403}  = "S�lectionner aucune piste.";
1499   $langs{tip404}  = "S�lectionner tout.";
1500   $langs{tip405}  = "Ajouter d\'autres fichiers � jouer.";
1501   $langs{tip406}  = "Fermer la configuration du player.";
1502   $langs{tip407}  = "Choose the interval in milliseconds that the text will be redrawed.";
1503   $langs{tip408}  = "Choose the step incremental in pixels.";
1504   $langs{tip409}  = "Scrolling text orientation: up/down or left/right.";
1505   $langs{tip410}  = "Show the xTunes like skin.\nTo take effect, change the display mode.";
1506   $langs{tip411}  = "Always On Top: keep the play mode window above the others windows.\nTo take effect, change the display mode.";
1507   $langs{tip429}  = "Play tracks that satisfies established filters can be more convenient or pleasant than the general random approach. ";
1508   $langs{tip429} .= "The available filters are 'Play Tracks From Different Albums/Artists/Genres/Years'.";
1509   $langs{tip429} .= "\n---***---\n";
1510   $langs{tip429} .= "For example, if the filter is set to Albums, then play tracks jumping between different Albums. ";
1511   $langs{tip429} .= "In this case, all Albums will be played before repeat some Album. ";
1512   $langs{tip429} .= "In others words, until one track has been played from each Album, the same Album will not be chosen twice.";
1513   $langs{tip429} .= "\n---***---\n";
1514   $langs{tip429} .= "When playing, the filters can be used considering the straight or random order. ";
1515
1516   $langs{tip501}  = "All tabs, messages, names and tips used by gnormalize.";
1517   $langs{tip502}  = "Vous devez de nouveau d�marrer.";
1518   $langs{tip503}  = "Guidance Language only used to help the translations.\nRead Only.";
1519   $langs{tip504}  = "Save the translations and new languages inside the directory: $lang_dir.";
1520   $langs{tip505}  = "Improve this translation or use it as base for a new Language.";
1521
1522
1523   $langs{msg001}  = "Fichiers support�s dans le r�pertoire (r�cursif):";
1524   $langs{msg002}  = "Fichiers support�s dans le r�pertoire (non r�cursif):";
1525   $langs{msg003}  = " Choisir un fichier mp3/mp4/mpc/ogg/ape/flac/wav/cda � normaliser!";
1526   $langs{msg004}  = " D�pendance non satisfaite!";
1527   $langs{msg005}  = "If Same Extension, Overwrite file";
1528   $langs{msg006}  = "Analyzing with ReplayGain normalization algorithms ...";
1529
1530   $langs{msg012} = ": lecture seule!";
1531   $langs{msg013} = " S�lectionner au moins une piste � ripper!";
1532   $langs{msg014} = " Succ�s! Rip fini.";
1533   $langs{msg015} = " Rip interrompu!";
1534   $langs{msg016} = " Succ�s!";
1535   $langs{msg017} = " Succ�s! Fichier converti.";
1536   $langs{msg018} = " Succ�s! Fichier normalis�.";
1537   $langs{msg019} = "Arr�ter le rip";
1538   $langs{msg020} = " R�cup�rer les infos de ";
1539   $langs{msg021} = " Attendez SVP. cdparanoia lit le cdrom ...";
1540   $langs{msg022} = " SVP, s�lectionnez au moins une piste � jouer.";
1541   $langs{msg023} = "Ne peut trouver \"";
1542   $langs{msg024} = "\" dans le chemin d'ex�cution.";
1543   $langs{msg025} = "La sortie ne peut �tre au format \"mp3\".";
1544   $langs{msg026} = "La sortie ne peut �tre au format \"ogg\".";
1545   $langs{msg027} = "La sortie ne peut �tre au format \"mpc\".";
1546   $langs{msg028} = "La sortie ne peut �tre au format \"ape\".";
1547   $langs{msg029} = "La sortie ne peut �tre au format \"flac\".";
1548   $langs{msg030} = "La sortie ne peut �tre au format \"mp4\".";
1549   $langs{msg031} = "Les wav ne peuvent �tre normalis�s.";
1550   $langs{msg032} = "L\'entr�e ne peut �tre au format \"ogg\"";
1551   $langs{msg033} = "L\'entr�e ne peut �tre au format \"mpc\".";
1552   $langs{msg034} = "L\'entr�e ne peut �tre au format \"mp4\".";
1553   $langs{msg035} = "cdcd est un player cd � la commande.";
1554   $langs{msg036} = "Premi�rement lancer \"cdcd\" � la console pour construire le fichier de configuration .cdcdrc";
1555   $langs{msg037} = "Choisir \"n\" � la question:  Etes-vous connect� � un r�seau (y/n) [y]?";
1556   $langs{msg038} = "Ne peut utiliser le module Perl \"";
1557   $langs{msg039} = "Ne peut lire les CD Audio.";
1558   $langs{msg040}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
1559   $langs{msg040} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
1560   $langs{msg040} .= "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. gnormalize can convert ";
1561   $langs{msg040} .= "audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
1562   $langs{msg041}  = "Ce fichier ne sera pas normalis�!";
1563   $langs{msg042} = "Non normalis� en accord avec l\'insensibilit�";
1564   $langs{msg043} = "Non normalis� car l\'ajustement ";
1565   $langs{msg044} = " est plus petit que la valeur d\'insensibilit�.";
1566   $langs{msg045} = " ne pas normaliser ";
1567   $langs{msg046} = "Encodage non n�cessaire";
1568   $langs{msg047} = " d�j� normalis� ";
1569   $langs{msg048} = "ce fichier est d�j� normalis�.";
1570   $langs{msg049} = " Arr�ter la normalisation!";
1571   $langs{msg050} = "Appliquer les ajustements de ";
1572   $langs{msg051} = " normalisation: ajustement de ";
1573   $langs{msg052} = "Niveaux de calculs...";
1574   $langs{msg053} = " normalis� avec ajustement de ";
1575   $langs{msg054} = " rip audio vers wav";
1576   $langs{msg055} = " Arr�ter le rip!";
1577   $langs{msg056} = "copie faite.";
1578   $langs{msg057} = " Stopper d�codage lame!";
1579   $langs{msg058} = " Stopper encodage lame!";
1580   $langs{msg061} = " Ces informations seront sauv�es dans le fichier normalis�.";
1581   $langs{msg062} = " tag sauv�.";
1582   $langs{msg063} = "S�lectionner un ficchier mp3/mp4/mpc/ogg/ape/flac/wav.";
1583   $langs{msg064} = "Homepage: $HOMEPAGE\nCe logiciel est disponible sous Licence Publique G�n�rale GNU.";
1584   $langs{msg065} = " sortie: ";
1585   $langs{msg066} = "fichier s�lectionn� - $langs{all_tab03} �ditable.";
1586   $langs{msg067} = " Impossible d\'ouvrir le cdrom ";
1587   $langs{msg068} = " R�cup�re l\'information de cddb ...";
1588   $langs{msg069} = " aucune information cddb.";
1589   $langs{msg070} = " presser le bouton sauver dans l'onglet \"$langs{all_tab03}\" pour sauver ces changements.";
1590   $langs{msg071} = " Non connect�, en utilisant ";
1591   $langs{msg072} = " cddb: internet inaccessible!";
1592   $langs{msg073} = " Ne peut ripper le CD Audio! cdparanoia introuvable.";
1593   $langs{msg074} = " Ne peut ripper le CD Audio! cdda2wav introuvable.";
1594   $langs{msg075} = "S�lectionner un fichier mp3, mp4, mpc, ogg, ape, flac, ou wav";
1595   $langs{msg077} = "r�glages";
1596   $langs{msg078} = "Fichier s�lectionn�";
1597   $langs{msg079} = "Version minimum recommand�e:";
1598   $langs{msg080} = " Option invalide!";
1599   $langs{msg081} = " Presser le bouton \"Rafra�chir le cdrom\" et r�-entrer les changements.";
1600   $langs{msg082} = "Aucune info cddb, installer le module perl \"CDDB_get\".";
1601   $langs{msg083} = "Not found MP3::Info perl module in your system.\n gnormalize will uses internal MP3::Info (version 1.20).";
1602   $langs{msg084}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
1603   $langs{msg084} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
1604   $langs{msg085}  = "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. ";
1605   $langs{msg085} .= "gnormalize can convert audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
1606   $langs{msg086} = "R�pertoire s�lectionn� (r�cursif)";
1607   $langs{msg087} = "S�lectionner un r�pertoire contenant un fichier mp3, mp4, mpc, ogg, ape, flac, ou wav.";
1608   $langs{msg088} = "R�pertoire s�lectionn� (non r�cursif)";
1609   $langs{msg089} = "Suppression des fichiers temporaires:";
1610   $langs{msg090} = "Ne peut pas trouver de r�pertoire ou fichier support�!";
1611   $langs{msg092} = "Enlever cette musique de la liste";
1612   $langs{msg093} = "Author: $AUTHOR\nEmail: $EMAIL\nDate: $DATE ; S�o Paulo - Brasil";
1613   $langs{msg094} = "Traduits vers fran�ais: Yaen Pujol et finis par Roger Gabriels Antwerp (Essen) Belgium\n";
1614   $langs{msg095} = "You must add new language name to translate!";
1615   }
1616
1617   if ( $language eq 'Nederlands' )
1618   {
1619   $langs{all_tab01} = "Data";
1620   $langs{all_tab02} = "Configuratie";
1621   $langs{all_tab03} = "Information";
1622   $langs{all_tab04} = "Rip/Play";
1623   $langs{all_tab05} = "Translations";
1624   $langs{all_tab06} = "Over";
1625   $langs{all_tab07} = "Option1";
1626   $langs{all_tab08} = "Option2";
1627   $langs{all_tab09} = "CDDB";
1628   $langs{all_tab10} = "Ripper";
1629   $langs{all_tab11} = "Colors";
1630   $langs{all_tab12} = "Players";
1631   $langs{all_tab13} = "Columns";
1632   $langs{all_tab14} = "Skins";
1633
1634   $langs{name001} = "Normalizatie Type";
1635   $langs{name002} = "Gevoeligheid";
1636   $langs{name003} = "Geselecteerd Bestand of Map";
1637   $langs{name004} = "Selecteer Invoer";
1638   $langs{name005} = "Output: Lossy Compression";
1639   $langs{name006} = "Output: Lossless Compression";
1640   $langs{name106} = "Output: Uncompressed";
1641
1642   $langs{name007} = "Bestand";
1643   $langs{name008} = "Recursief";
1644   $langs{name009} = "Delete Wav Files";
1645   $langs{name010} = "Change Properties";
1646   $langs{name011} = "Overwrite";
1647   $langs{name012} = "Priority";
1648   $langs{name013} = "Add Command";
1649   $langs{name014} = "Filename Rip";
1650   $langs{name015} = "Character";
1651   $langs{name016} = "Idiom";
1652   $langs{name017} = "Font";
1653   $langs{name018} = "Read CDDB";
1654   $langs{name019} = "Overwrite CDDB";
1655   $langs{name020} = "CDDB Server";
1656   $langs{name021} = "Port";
1657   $langs{name022} = "Transport";
1658   $langs{name023} = "disable paranoia";
1659   $langs{name024} = "disable extra paranoia";
1660   $langs{name025} = "abort on skip";
1661   $langs{name026} = "never skip repair";
1662   $langs{name027} = "paranoia no-verify";
1663   $langs{name028} = "paranoia disable";
1664   $langs{name029} = "ripper";
1665   $langs{name030} = "Encode Type";
1666   $langs{name031} = "Mode";
1667   $langs{name032} = "Encode Quality";
1668   $langs{name033} = "Variable";
1669   $langs{name034} = "Title";
1670   $langs{name035} = "Artist";
1671   $langs{name036} = "Album";
1672   $langs{name037} = "Comment";
1673   $langs{name038} = "Year";
1674   $langs{name039} = "Track Number";
1675   $langs{name040} = "Genre";
1676   $langs{name041} = "Frequency";
1677   $langs{name042} = "Time";
1678   $langs{name043} = "Channels";
1679   $langs{name044} = "Length";
1680   $langs{name045} = "Track";
1681   $langs{name046} = "Artist";
1682   $langs{name047} = "Title";
1683   $langs{name048} = "Size";
1684   $langs{name049} = "Year";
1685   $langs{name050} = "Filepath";
1686   $langs{name051} = "Extension";
1687   $langs{name052} = "Selecteer Uitvoer";
1688   $langs{name053} = "Album";
1689   $langs{name054} = "Play Count";
1690   $langs{name055} = "Animation";
1691   $langs{name056} = "Filename";
1692   $langs{name057} = "Show Tooltips";
1693   $langs{name058} = "Guidance Language";
1694   $langs{name059} = "Current Translation";
1695   $langs{name060} = "Improve The Currente Language Translation";
1696   $langs{name061} = "Add New Language";
1697   $langs{name062} = "Add new language here and then press save buton.";
1698   $langs{name063} = "Normaliseer";
1699   $langs{name064} = "CD Player";
1700   $langs{name065} = "Play";
1701
1702   $langs{name429} = 'Play Tracks Without Any Filters';
1703   $langs{name430} = 'Play Tracks From Different Albums';
1704   $langs{name431} = 'Play Tracks From Different Artists';
1705   $langs{name432} = 'Play Tracks From Different Genres';
1706   $langs{name433} = 'Play Tracks From Different Years';
1707
1708   $langs{tip101}  = "Track: use ReplayGain Single Track gain setting.\nAlbum: don't implemented yet!\n";
1709   $langs{tip101} .= "None: no normalization is done.\n";
1710   $langs{tip101} .= "---*---\n";
1711   $langs{tip101} .= "gnormalize uses the wavegain to normalize wave files.\n";
1712   $langs{tip101} .= "wavegain is a program that applies ReplayGain normalization algorithms directly to wave files.";
1713   $langs{tip102}  = "Apply additional Manual Gain adjustment in decibels between -12.0 and +12.0. ";
1714   $langs{tip102} .= "Gain = 0.00dB correspond to 89dB standard or reference value.\n";
1715   $langs{tip102} .= "---*---\n";
1716   $langs{tip102} .= "Basic ideas of ReplayGain normalization algorithms:\n";
1717   $langs{tip102} .= "1. Calculate the Root Mean Square (RMS) of the waveform every 50ms;\n";
1718   $langs{tip102} .= "2. Sort the RMS values into numerical order, and choose the value 95% up the list to represent the overall perceived loudness;\n";
1719   $langs{tip102} .= "3. Calibrate the energy of a digital signal to 89dB standard value and apply the gain adjustment.\n";
1720   $langs{tip102} .= "---*---\n";
1721   $langs{tip102} .= "RMS(A) = sqrt(mean(A^2)) is the Root Mean Square of sound amplitude A.\n";
1722   $langs{tip102} .= "Gain(A) = 2*10*log(A/A0), such that A is the measured amplitude and A0 is the reference amplitude.";
1723   $langs{tip103}  = "Insensibility of normalize. Only not re-encode if:\n";
1724   $langs{tip103} .= "1) the extension of input file is the same of output file;\n";
1725   $langs{tip103} .= "2) and \"$langs{name010}\" button is not active;\n";
1726   $langs{tip103} .= "3) and adjust of normalize is less than insensibility value.";
1727   $langs{tip104}  = "Select one directory with supported file.";
1728   $langs{tip105}  = "Choose the directory where all the files will be saved.";
1729   $langs{tip106}  = "Normalize/Convert all supported files inside the directory (recursively or not).";
1730   $langs{tip107}  = "Select a directory including subdirectories: descende recursively into the directory preserving the same structure.";
1731   $langs{tip108}  = "After normalize, the output format will be mp3. Play it with mpg123, mpg321 or madplay.";
1732   $langs{tip109}  = "After normalize, the output format will be mp4 (also know as m4a) or aac. Play it with mplayer.";
1733   $langs{tip110}  = "After normalize, the output format will be mpc (also know as mpp or mp+). Play it with mppdec or xmms plugin.";
1734   $langs{tip111}  = "After normalize, the output format will be ogg. Play it with ogg123.";
1735   $langs{tip112}  = "After normalize, the output format will be ape. Play it with the command:\n   mac \"song.ape\"  - -d | play -t wav - ";
1736   $langs{tip113}  = "After normalize, the output format will be flac. Play it with flac123 or xmms plugin.\nRequired flac version >= \"1.1.1\".";
1737   $langs{tip114}  = "This is suitable to make an audio cd.\nWav is an uncompressed audio format.";
1738   $langs{tip115}  = "NOTE:\n";
1739   $langs{tip115} .= "This compression technique is known as 'Lossy Compression' whereby during the compression data can be lost. ";
1740   $langs{tip115} .= "The degree of loss varies upon settings. Although data loss does occur, it is minimal and certainly not noticable ";
1741   $langs{tip115} .= "when using good settings.\nExamples of Lossy compression audio: Mp3, Mp4, Mpc and Ogg.";
1742   $langs{tip116}  = "NOTE:\n";
1743   $langs{tip116} .= "This compression technique is known as 'Lossless Compression' in which all of the information in the original ";
1744   $langs{tip116} .= "data is preserved, and the original data may be recovered in form identical to its original form.\n";
1745   $langs{tip116} .= "Examples of Lossless compression audio: Ape and Flac.";
1746   $langs{tip117}  = "Show the progress bar.";
1747   $langs{tip118}  = "Hide the progress bar.";
1748   $langs{tip119}  = "Show the command line and others info.";
1749   $langs{tip120}  = "Hide the command line and others info.";
1750   $langs{tip121}  = "Quit.";
1751   $langs{tip122}  = "Clear all messages.";
1752
1753   $langs{tip201}  = "After the normalize process, the temporary wav files will be deleted.";
1754   $langs{tip202}  = "After normalize the files, you can change its encode properties by determining the encode type, the bitrate of all ";
1755   $langs{tip202} .= "output files, etc. Otherwise, the bitrate of each input file and others properties will be conserved.";
1756   $langs{tip203}  = "The normalized file will be saved in the same directory of input. If it already exist then overwrite the original file.";
1757   $langs{tip204}  = "Show the hints and informations.";
1758   $langs{tip205}  = "Show the Tux and others penguins animations.";
1759   $langs{tip206}  = "Modify scheduling priority: 0 (highest priority) to 19 (lowest)";
1760   $langs{tip207}  = "Output filename format used only for rip Audio CD:\n";
1761   $langs{tip207} .= "   %a - artist\n";
1762   $langs{tip207} .= "   %b - album\n";
1763   $langs{tip207} .= "   %t - song title\n";
1764   $langs{tip207} .= "   %n - track number\n";
1765   $langs{tip207} .= "   %y - year\n";
1766   $langs{tip207} .= "   %g - genre";
1767   $langs{tip208}  = "Advised font: Sans 10.";
1768   $langs{tip209}  = "Read the CDDB info for Audio CD in your drive from CDDB server \"$CDDB_server\" or from files saved into $home/.cddd/.";
1769   $langs{tip210}  = "Always get CDDB info from server and overwrite the CDDB file saved at $home/.cddd/";
1770   $langs{tip211}  = "Some servers:\nfreedb.freedb.org:8880\nus.freedb.org:8880\ncddb.com\nus.cddb.com:8880\nsunsite.unc.edu:8880";
1771   $langs{tip212}  = "Choose the port number.";
1772   $langs{tip213}  = "Disable all data verification and correction features. Consequently the extraction/rip is more fast. ";
1773   $langs{tip213} .= "This option implies that -Y is active. See cdparanoia (option -Z).";
1774   $langs{tip214}  = "Do not accept any skips; retry forever if needed. See cdparanoia (option -z).";
1775   $langs{tip215}  = "Disables intra-read data verification; only overlap checking at read boundaries is performed. Not recommended. See cdparanoia (option -Y).\n";
1776   $langs{tip215} .= "Boundaries are spaces/limit inter tracks of an audio cd. cdparanoia reads this boundaries carefully when rip on track.";
1777   $langs{tip216}  = "If the read skips due to imperfect data, a scratch, whatever, abort reading this track. See cdparanoia (option -X).";
1778   $langs{tip217}  = "Use the paranoia library instead of cdda2wav's routines for reading. See cdda2wav (option -paranoia).";
1779   $langs{tip218}  = "Disables paranoia mode. Paranoia is still being used. See cdda2wav (option -paraopts=help).";
1780   $langs{tip219}  = "Switches verify off, and overlap on.";
1781   $langs{tip220}  = "Choose a targeted average bitrate. Sets encoding to the bitrate closest to this value (in Kb/s).";
1782   $langs{tip221}  = "Adjust the quality of mp3; 9 is the worst quality, 0 is the best quality with more Kb/s. See: man lame (option: -V).";
1783   $langs{tip222}  = "Adjust the quality of encode for ogg; 10 is the best quality with more Kb/s used, -1 is the worst quality. See: man oggenc.";
1784   $langs{tip223}  = "Adjust the quality of lame encoder for mp3 files: 0 is the best quality with maximum ";
1785   $langs{tip223} .= "algorithms process; 9 is the worst quality but more fast. See: man lame (option: -q).";
1786   $langs{tip224}  = "Set default variable bitrate (VBR) quantizer quality in percent for mp4 format; 500 is the best ";
1787   $langs{tip224} .= "quality with more Kb/s used; 10 is the worst quality. See: <faac --help>.";
1788   $langs{tip225}  =  "Adjust the quality of encode for mpc using profiles; 8 (braindead) is the best quality with more Kb/s used,";
1789   $langs{tip225} .= " 2 (telephone) is the worst quality. See: mppenc --longhelp\n";
1790   $langs{tip225} .= " q=2 --telephone - lowest quality,       (32... 48 kbps)\n";
1791   $langs{tip225} .= " q=3 --thumb     - low quality/internet, (58... 86 kbps)\n";
1792   $langs{tip225} .= " q=4 --radio     - medium (MP3) quality, (112...152 kbps)\n";
1793   $langs{tip225} .= " q=5 --standard  - high quality (dflt),  (142...184 kbps)\n";
1794   $langs{tip225} .= " q=6 --xtreme    - extreme high quality, (168...212 kbps)\n";
1795   $langs{tip225} .= " q=7 --insane    - extreme high quality, (232...268 kbps)\n";
1796   $langs{tip225} .= " q=8 --braindead - extreme high quality, (232...278 kbps)";
1797   $langs{tip226}  = "Compression level:\n  fast: 1000\n  normal: 2000\n  high: 3000\n  extra high: 4000\n  insane: 5000\n";
1798   $langs{tip226} .= "\nMore compression level \nresults small files.\nSee the help: mac";
1799   $langs{tip227}  = "Compression level:\n  fast: 0\n  normal: 5\n  best: 8\nSee <man flac>.";
1800   $langs{tip228}  = "Put here the additional command line used to encode this files.";
1801   $langs{tip229}  = "Used to encode ID3v2.3.0 Tag for MP3 files.";
1802   $langs{tip250}  = "If this entry is empty, the original comment tag will be preserved. ";
1803   $langs{tip250} .= "The default comment is \"gnormalize - gain=%gain\" with %gain replaced by its value.";
1804
1805   $langs{tip301}  = "If there is more than one artist, use:\nArtist1 & Artist2 & Artist3 & ...";
1806   $langs{tip302}  = "Track number";
1807   $langs{tip303}  = "Total track number";
1808   $langs{tip304}  = "Save changes of tag.";
1809   $langs{tip305}  = "Save changes of tag to $home/.cddb directory.";
1810
1811   $langs{tip401}  = "Try /dev/cdrom, /dev/dvd, /dev/hdc or ...\nSee \"/etc/fstab\" file.";
1812   $langs{tip402}  = "Refresh the cdrom drive and erase the playlist.";
1813   $langs{tip403}  = "Unselect all tracks.";
1814   $langs{tip404}  = "Select all tracks.";
1815   $langs{tip405}  = "Add more files to play.";
1816   $langs{tip406}  = "Close the gnormalize player config.";
1817   $langs{tip407}  = "Choose the interval in milliseconds that the text will be redrawed.";
1818   $langs{tip408}  = "Choose the step incremental in pixels.";
1819   $langs{tip409}  = "Scrolling text orientation: up/down or left/right.";
1820   $langs{tip410}  = "Show the xTunes like skin.\nTo take effect, change the display mode.";
1821   $langs{tip411}  = "Always On Top: keep the play mode window above the others windows.\nTo take effect, change the display mode.";
1822   $langs{tip429}  = "Play tracks that satisfies established filters can be more convenient or pleasant than the general random approach. ";
1823   $langs{tip429} .= "The available filters are 'Play Tracks From Different Albums/Artists/Genres/Years'.";
1824   $langs{tip429} .= "\n---***---\n";
1825   $langs{tip429} .= "For example, if the filter is set to Albums, then play tracks jumping between different Albums. ";
1826   $langs{tip429} .= "In this case, all Albums will be played before repeat some Album. ";
1827   $langs{tip429} .= "In others words, until one track has been played from each Album, the same Album will not be chosen twice.";
1828   $langs{tip429} .= "\n---***---\n";
1829   $langs{tip429} .= "When playing, the filters can be used considering the straight or random order. ";
1830
1831   $langs{tip501}  = "All tabs, messages, names and tips used by gnormalize.";
1832   $langs{tip502}  = "You need to restart the Gnormalize.";
1833   $langs{tip503}  = "Guidance Language only used to help the translations.\nRead Only.";
1834   $langs{tip504}  = "Save the translations and new languages inside the directory: $lang_dir.";
1835   $langs{tip505}  = "Improve this translation or use it as base for a new Language.";
1836
1837
1838   $langs{msg001}  = "Supported Files into the directory (recursively):";
1839   $langs{msg002}  = "Supported Files into the directory (not recursively):";
1840   $langs{msg003}  = " Choose a mp3/mp4/mpc/ogg/ape/flac/wav/cda file to normalize!";
1841   $langs{msg004}  = " Dependence not satisfied!";
1842   $langs{msg005}  = "If Same Extension, Overwrite file";
1843   $langs{msg006}  = "Analyzing with ReplayGain normalization algorithms ...";
1844
1845   $langs{msg012} = ": read only!";
1846   $langs{msg013} = " Select at least one track to rip!";
1847   $langs{msg014} = " Success! Rip finished.";
1848   $langs{msg015} = " Rip stopped!";
1849   $langs{msg016} = " Success!";
1850   $langs{msg017} = " Success! File converted.";
1851   $langs{msg018} = " Success! File normalized.";
1852   $langs{msg019} = "stop ripping";
1853   $langs{msg020} = " Get info from ";
1854   $langs{msg021} = " Please, wait. cdparanoia is reading the cdrom ...";
1855   $langs{msg022} = " Please, select at least one track to play.";
1856   $langs{msg023} = "Can't find \"";
1857   $langs{msg024} = "\" in executable path.";
1858   $langs{msg025} = "The output can't be \"mp3\" format.";
1859   $langs{msg026} = "The output can't be \"ogg\" format.";
1860   $langs{msg027} = "The output can't be \"mpc\" format.";
1861   $langs{msg028} = "The output can't be \"ape\" format.";
1862   $langs{msg029} = "The output can't be \"flac\" format.";
1863   $langs{msg030} = "The output can't be \"mp4\" format.";
1864   $langs{msg031} = "The wav files can't be normalized.";
1865   $langs{msg032} = "The input can't be \"ogg\" format.";
1866   $langs{msg033} = "The input can't be \"mpc\" format.";
1867   $langs{msg034} = "The input can't be \"mp4\" format.";
1868   $langs{msg035} = "cdcd is a Command Driven CD player.";
1869   $langs{msg036} = "First run \"cdcd\" in command line to make the config file .cdcdrc";
1870   $langs{msg037} = "Choose \"n\" for the question:  Are you connected to a network (y/n) [y]?";
1871   $langs{msg038} = "Can't use the perl module \"";
1872   $langs{msg039} = "Can't play audio CD.";
1873   $langs{msg040}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
1874   $langs{msg040} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
1875   $langs{msg040} .= "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. gnormalize can convert ";
1876   $langs{msg040} .= "audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
1877   $langs{msg041}  = "This file will not be normalized!";
1878   $langs{msg042} = "According to insensibility, not normalize";
1879   $langs{msg043} = "Not normalize because adjustment ";
1880   $langs{msg044} = " is less than insensibility value.";
1881   $langs{msg045} = " not normalize ";
1882   $langs{msg046} = "Don't need encode";
1883   $langs{msg047} = " already normalized ";
1884   $langs{msg048} = "This file is already normalized.";
1885   $langs{msg049} = " Stop normalizing!";
1886   $langs{msg050} = "Applying adjustment of ";
1887   $langs{msg051} = " normalizing: adjustment of ";
1888   $langs{msg052} = "Computing levels...";
1889   $langs{msg053} = " normalized with adjustment of ";
1890   $langs{msg054} = " ripping audio to wav";
1891   $langs{msg055} = " Stop ripping!";
1892   $langs{msg056} = "copy maked.";
1893   $langs{msg057} = " Stop lame decoding!";
1894   $langs{msg058} = " Stop lame encoding!";
1895   $langs{msg061} = " This informations will be saved on normalized file.";
1896   $langs{msg062} = " tag saved.";
1897   $langs{msg063} = "Select a mp3/mp4/mpc/ogg/ape/flac/wav file.";
1898   $langs{msg064} = "Homepage: $HOMEPAGE\nThis software is available under GNU General Public Licence.";
1899   $langs{msg065} = " output: ";
1900   $langs{msg066} = "file selected - $langs{all_tab03} can be edited.";
1901   $langs{msg067} = " Unable to open cdrom drive ";
1902   $langs{msg068} = " Getting information from cddb ...";
1903   $langs{msg069} = " none cddb information.";
1904   $langs{msg070} = " Press save button on \"$langs{all_tab03}\" tab to save this changes.";
1905   $langs{msg071} = " Not connected, using ";
1906   $langs{msg072} = " cddb: can't connect to internet!";
1907   $langs{msg073} = " Can't rip audio CD! cdparanoia not found.";
1908   $langs{msg074} = " Can't rip audio CD! cdda2wav not found.";
1909   $langs{msg075} = "Select a mp3, mp4, mpc, ogg, ape, flac, or wav file";
1910   $langs{msg077} = "settings";
1911   $langs{msg078} = "Selected file";
1912   $langs{msg079} = "Recomended minimum version:";
1913   $langs{msg080} = " Invalid option!";
1914   $langs{msg081} = " Press the \"Refresh the cdrom\" button and type again the changes.";
1915   $langs{msg082} = "Not found CDDB_get perl module in your system.\n gnormalize will uses internal CDDB_get (version 2.27).";
1916   $langs{msg083} = "Not found MP3::Info perl module in your system.\n gnormalize will uses internal MP3::Info (version 1.20).";
1917   $langs{msg084}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
1918   $langs{msg084} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
1919   $langs{msg085}  = "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. ";
1920   $langs{msg085} .= "gnormalize can convert audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
1921   $langs{msg086} = "Selected Directory (recursively)";
1922   $langs{msg087} = "Select a directory containing mp3, mp4, mpc, ogg, ape, flac, or wav file.";
1923   $langs{msg088} = "Selected Directory (not recursively)";
1924   $langs{msg089} = "Removing temporary file:";
1925   $langs{msg090} = "Can not find no supported file inside of directory!";
1926   $langs{msg092} = "remove this music from list";
1927   $langs{msg093} = "Author: $AUTHOR\nEmail: $EMAIL\nDate: $DATE ; S�o Paulo - Brasil";
1928   $langs{msg094}  = "Nederlandse vertaling door Roger Gabriels Antwerp (Essen) Belgium\n";
1929   $langs{msg094} .= "E-mail: normaliseer\@pcfreesoft.com | Homepage: htp://www.pcfreesoft.com";
1930   $langs{msg095} = "You must add new language name to translate!";
1931   }
1932
1933   if ( $language eq 'Espag�ol' )
1934   {
1935   $langs{all_tab01} = "Datos";
1936   $langs{all_tab02} = "Configuraci�n";
1937   $langs{all_tab03} = "Information";
1938   $langs{all_tab04} = "Rip/Play";
1939   $langs{all_tab05} = "Translations";
1940   $langs{all_tab06} = "Sobre";
1941   $langs{all_tab07} = "Option1";
1942   $langs{all_tab08} = "Option2";
1943   $langs{all_tab09} = "CDDB";
1944   $langs{all_tab10} = "Ripper";
1945   $langs{all_tab11} = "Colors";
1946   $langs{all_tab12} = "Players";
1947   $langs{all_tab13} = "Columns";
1948   $langs{all_tab14} = "Skins";
1949
1950   $langs{name001} = "Tipo de Normalizaci�n";
1951   $langs{name002} = "Insensibilidad";
1952   $langs{name003} = "Archivo Seleccionado";
1953   $langs{name004} = "Repertorio de Entrada";
1954   $langs{name005} = "Output: Lossy Compression";
1955   $langs{name006} = "Output: Lossless Compression";
1956   $langs{name106} = "Output: Uncompressed";
1957
1958   $langs{name007} = "Archivo";
1959   $langs{name008} = "Recursivamente";
1960   $langs{name009} = "Delete Wav Files";
1961   $langs{name010} = "Change Properties";
1962   $langs{name011} = "Overwrite";
1963   $langs{name012} = "Priority";
1964   $langs{name013} = "Add Command";
1965   $langs{name014} = "Filename Rip";
1966   $langs{name015} = "Character";
1967   $langs{name016} = "Idiom";
1968   $langs{name017} = "Font";
1969   $langs{name018} = "Read CDDB";
1970   $langs{name019} = "Overwrite CDDB";
1971   $langs{name020} = "CDDB Server";
1972   $langs{name021} = "Port";
1973   $langs{name022} = "Transport";
1974   $langs{name023} = "disable paranoia";
1975   $langs{name024} = "disable extra paranoia";
1976   $langs{name025} = "abort on skip";
1977   $langs{name026} = "never skip repair";
1978   $langs{name027} = "paranoia no-verify";
1979   $langs{name028} = "paranoia disable";
1980   $langs{name029} = "ripper";
1981   $langs{name030} = "Encode Type";
1982   $langs{name031} = "Mode";
1983   $langs{name032} = "Encode Quality";
1984   $langs{name033} = "Variable";
1985   $langs{name034} = "Title";
1986   $langs{name035} = "Artist";
1987   $langs{name036} = "Album";
1988   $langs{name037} = "Comment";
1989   $langs{name038} = "Year";
1990   $langs{name039} = "Track Number";
1991   $langs{name040} = "Genre";
1992   $langs{name041} = "Frequency";
1993   $langs{name042} = "Time";
1994   $langs{name043} = "Channels";
1995   $langs{name044} = "Length";
1996   $langs{name045} = "Track";
1997   $langs{name046} = "Artist";
1998   $langs{name047} = "Title";
1999   $langs{name048} = "Size";
2000   $langs{name049} = "Year";
2001   $langs{name050} = "Filepath";
2002   $langs{name051} = "Extension";
2003   $langs{name052} = "Repertorio de Salida";
2004   $langs{name053} = "Album";
2005   $langs{name054} = "Play Count";
2006   $langs{name055} = "Animation";
2007   $langs{name056} = "Filename";
2008   $langs{name057} = "Show Tooltips";
2009   $langs{name058} = "Guidance Language";
2010   $langs{name059} = "Current Translation";
2011   $langs{name060} = "Improve The Currente Language Translation";
2012   $langs{name061} = "Add New Language";
2013   $langs{name062} = "Add new language here and then press save buton.";
2014   $langs{name063} = "Normalizaci�n";
2015   $langs{name064} = "CD Player";
2016   $langs{name065} = "Play";
2017
2018   $langs{name429} = 'Play Tracks Without Any Filters';
2019   $langs{name430} = 'Play Tracks From Different Albums';
2020   $langs{name431} = 'Play Tracks From Different Artists';
2021   $langs{name432} = 'Play Tracks From Different Genres';
2022   $langs{name433} = 'Play Tracks From Different Years';
2023
2024   $langs{tip101}  = "Track: use ReplayGain Single Track gain setting.\nAlbum: don't implemented yet!\n";
2025   $langs{tip101} .= "None: no normalization is done.\n";
2026   $langs{tip101} .= "---*---\n";
2027   $langs{tip101} .= "gnormalize uses the wavegain to normalize wave files.\n";
2028   $langs{tip101} .= "wavegain is a program that applies ReplayGain normalization algorithms directly to wave files.";
2029   $langs{tip102}  = "Apply additional Manual Gain adjustment in decibels between -12.0 and +12.0. ";
2030   $langs{tip102} .= "Gain = 0.00dB correspond to 89dB standard or reference value.\n";
2031   $langs{tip102} .= "---*---\n";
2032   $langs{tip102} .= "Basic ideas of ReplayGain normalization algorithms:\n";
2033   $langs{tip102} .= "1. Calculate the Root Mean Square (RMS) of the waveform every 50ms;\n";
2034   $langs{tip102} .= "2. Sort the RMS values into numerical order, and choose the value 95% up the list to represent the overall perceived loudness;\n";
2035   $langs{tip102} .= "3. Calibrate the energy of a digital signal to 89dB standard value and apply the gain adjustment.\n";
2036   $langs{tip102} .= "---*---\n";
2037   $langs{tip102} .= "RMS(A) = sqrt(mean(A^2)) is the Root Mean Square of sound amplitude A.\n";
2038   $langs{tip102} .= "Gain(A) = 2*10*log(A/A0), such that A is the measured amplitude and A0 is the reference amplitude.";
2039   $langs{tip103}  = "Insensibility of normalize. Only not re-encode if:\n";
2040   $langs{tip103} .= "1) the extension of input file is the same of output file;\n";
2041   $langs{tip103} .= "2) and \"$langs{name010}\" button is not active;\n";
2042   $langs{tip103} .= "3) and adjust of normalize is less than insensibility value.";
2043   $langs{tip104}  = "Select one directory with supported file.";
2044   $langs{tip105}  = "Choose the directory where all the files will be saved.";
2045   $langs{tip106}  = "Normalize/Convert all supported files inside the directory (recursively or not).";
2046   $langs{tip107}  = "Select a directory including subdirectories: descende recursively into the directory preserving the same structure.";
2047   $langs{tip108}  = "After normalize, the output format will be mp3. Play it with mpg123, mpg321 or madplay.";
2048   $langs{tip109}  = "After normalize, the output format will be mp4 (also know as m4a) or aac. Play it with mplayer.";
2049   $langs{tip110}  = "After normalize, the output format will be mpc (also know as mpp or mp+). Play it with mppdec or xmms plugin.";
2050   $langs{tip111}  = "After normalize, the output format will be ogg. Play it with ogg123.";
2051   $langs{tip112}  = "After normalize, the output format will be ape. Play it with the command:\n   mac \"song.ape\"  - -d | play -t wav - ";
2052   $langs{tip113}  = "After normalize, the output format will be flac. Play it with flac123 or xmms plugin.\nRequired flac version >= \"1.1.1\".";
2053   $langs{tip114}  = "Esta opcic�n es muy corriente Para grabar un CD audio.\nWav no es un audio comprimido.";
2054   $langs{tip115}  = "NOTE:\n";
2055   $langs{tip115} .= "This compression technique is known as 'Lossy Compression' whereby during the compression data can be lost. ";
2056   $langs{tip115} .= "The degree of loss varies upon settings. Although data loss does occur, it is minimal and certainly not noticable ";
2057   $langs{tip115} .= "when using good settings.\nExamples of Lossy compression audio: Mp3, Mp4, Mpc and Ogg.";
2058   $langs{tip116}  = "NOTE:\n";
2059   $langs{tip116} .= "This compression technique is known as 'Lossless Compression' in which all of the information in the original ";
2060   $langs{tip116} .= "data is preserved, and the original data may be recovered in form identical to its original form.\n";
2061   $langs{tip116} .= "Examples of Lossless compression audio: Ape and Flac.";
2062   $langs{tip117}  = "Show the progress bar.";
2063   $langs{tip118}  = "Hide the progress bar.";
2064   $langs{tip119}  = "Show the command line and others info.";
2065   $langs{tip120}  = "Hide the command line and others info.";
2066   $langs{tip121}  = "Quit.";
2067   $langs{tip122}  = "Clear all messages.";
2068
2069   $langs{tip201}  = "After the normalize process, the temporary wav files will be deleted.";
2070   $langs{tip202}  = "After normalize the files, you can change its encode properties by determining the encode type, the bitrate of all ";
2071   $langs{tip202} .= "output files, etc. Otherwise, the bitrate of each input file and others properties will be conserved.";
2072   $langs{tip203}  = "The normalized file will be saved in the same directory of input. If it already exist then overwrite the original file.";
2073   $langs{tip204}  = "Show the hints and informations.";
2074   $langs{tip205}  = "Show the Tux and others penguins animations.";
2075   $langs{tip206}  = "Modify scheduling priority: 0 (highest priority) to 19 (lowest)";
2076   $langs{tip207}  = "Output filename format used only for rip Audio CD:\n";
2077   $langs{tip207} .= "   %a - artist\n";
2078   $langs{tip207} .= "   %b - album\n";
2079   $langs{tip207} .= "   %t - song title\n";
2080   $langs{tip207} .= "   %n - track number\n";
2081   $langs{tip207} .= "   %y - year\n";
2082   $langs{tip207} .= "   %g - genre";
2083   $langs{tip208}  = "Advised font: Sans 10.";
2084   $langs{tip209}  = "Read the CDDB info for Audio CD in your drive from CDDB server \"$CDDB_server\" or from files saved into $home/.cddd/.";
2085   $langs{tip210}  = "Always get CDDB info from server and overwrite the CDDB file saved at $home/.cddd/";
2086   $langs{tip211}  = "Some servers:\nfreedb.freedb.org:8880\nus.freedb.org:8880\ncddb.com\nus.cddb.com:8880\nsunsite.unc.edu:8880";
2087   $langs{tip212}  = "Choose the port number.";
2088   $langs{tip213}  = "Disable all data verification and correction features. Consequently the extraction/rip is more fast. ";
2089   $langs{tip213} .= "This option implies that -Y is active. See cdparanoia (option -Z).";
2090   $langs{tip214}  = "Do not accept any skips; retry forever if needed. See cdparanoia (option -z).";
2091   $langs{tip215}  = "Disables intra-read data verification; only overlap checking at read boundaries is performed. Not recommended. See cdparanoia (option -Y).\n";
2092   $langs{tip215} .= "Boundaries are spaces/limit inter tracks of an audio cd. cdparanoia reads this boundaries carefully when rip on track.";
2093   $langs{tip216}  = "If the read skips due to imperfect data, a scratch, whatever, abort reading this track. See cdparanoia (option -X).";
2094   $langs{tip217}  = "Use the paranoia library instead of cdda2wav's routines for reading. See cdda2wav (option -paranoia).";
2095   $langs{tip218}  = "Disables paranoia mode. Paranoia is still being used. See cdda2wav (option -paraopts=help).";
2096   $langs{tip219}  = "Switches verify off, and overlap on.";
2097   $langs{tip220}  = "Choose a targeted average bitrate. Sets encoding to the bitrate closest to this value (in Kb/s).";
2098   $langs{tip221}  = "Adjust the quality of mp3; 9 is the worst quality, 0 is the best quality with more Kb/s. See: man lame (option: -V).";
2099   $langs{tip222}  = "Adjust the quality of encode for ogg; 10 is the best quality with more Kb/s used, -1 is the worst quality. See: man oggenc.";
2100   $langs{tip223}  = "Adjust the quality of lame encoder for mp3 files: 0 is the best quality with maximum ";
2101   $langs{tip223} .= "algorithms process; 9 is the worst quality but more fast. See: man lame (option: -q).";
2102   $langs{tip224}  = "Set default variable bitrate (VBR) quantizer quality in percent for mp4 format; 500 is the best ";
2103   $langs{tip224} .= "quality with more Kb/s used; 10 is the worst quality. See: <faac --help>.";
2104   $langs{tip225}  =  "Adjust the quality of encode for mpc using profiles; 8 (braindead) is the best quality with more Kb/s used,";
2105   $langs{tip225} .= " 2 (telephone) is the worst quality. See: mppenc --longhelp\n";
2106   $langs{tip225} .= " q=2 --telephone - lowest quality,       (32... 48 kbps)\n";
2107   $langs{tip225} .= " q=3 --thumb     - low quality/internet, (58... 86 kbps)\n";
2108   $langs{tip225} .= " q=4 --radio     - medium (MP3) quality, (112...152 kbps)\n";
2109   $langs{tip225} .= " q=5 --standard  - high quality (dflt),  (142...184 kbps)\n";
2110   $langs{tip225} .= " q=6 --xtreme    - extreme high quality, (168...212 kbps)\n";
2111   $langs{tip225} .= " q=7 --insane    - extreme high quality, (232...268 kbps)\n";
2112   $langs{tip225} .= " q=8 --braindead - extreme high quality, (232...278 kbps)";
2113   $langs{tip226}  = "Compression level:\n  fast: 1000\n  normal: 2000\n  high: 3000\n  extra high: 4000\n  insane: 5000\n";
2114   $langs{tip226} .= "\nMore compression level \nresults small files.\nSee the help: mac";
2115   $langs{tip227}  = "Compression level:\n  fast: 0\n  normal: 5\n  best: 8\nSee <man flac>.";
2116   $langs{tip228}  = "Put here the additional command line used to encode this files.";
2117   $langs{tip229}  = "Used to encode ID3v2.3.0 Tag for MP3 files.";
2118   $langs{tip250}  = "If this entry is empty, the original comment tag will be preserved. ";
2119   $langs{tip250} .= "The default comment is \"gnormalize - gain=%gain\" with %gain replaced by its value.";
2120
2121   $langs{tip301}  = "If there is more than one artist, use:\nArtist1 & Artist2 & Artist3 & ...";
2122   $langs{tip302}  = "Track number";
2123   $langs{tip303}  = "Total track number";
2124   $langs{tip304}  = "Save changes of tag.";
2125   $langs{tip305}  = "Save changes of tag to $home/.cddb directory.";
2126
2127   $langs{tip401}  = "Try /dev/cdrom, /dev/dvd, /dev/hdc or ...\nSee \"/etc/fstab\" file.";
2128   $langs{tip402}  = "Refresh the cdrom drive and erase the playlist.";
2129   $langs{tip403}  = "Unselect all tracks.";
2130   $langs{tip404}  = "Select all tracks.";
2131   $langs{tip405}  = "Add more files to play.";
2132   $langs{tip406}  = "Close the gnormalize player config.";
2133   $langs{tip407}  = "Choose the interval in milliseconds that the text will be redrawed.";
2134   $langs{tip408}  = "Choose the step incremental in pixels.";
2135   $langs{tip409}  = "Scrolling text orientation: up/down or left/right.";
2136   $langs{tip410}  = "Show the xTunes like skin.\nTo take effect, change the display mode.";
2137   $langs{tip411}  = "Always On Top: keep the play mode window above the others windows.\nTo take effect, change the display mode.";
2138   $langs{tip429}  = "Play tracks that satisfies established filters can be more convenient or pleasant than the general random approach. ";
2139   $langs{tip429} .= "The available filters are 'Play Tracks From Different Albums/Artists/Genres/Years'.";
2140   $langs{tip429} .= "\n---***---\n";
2141   $langs{tip429} .= "For example, if the filter is set to Albums, then play tracks jumping between different Albums. ";
2142   $langs{tip429} .= "In this case, all Albums will be played before repeat some Album. ";
2143   $langs{tip429} .= "In others words, until one track has been played from each Album, the same Album will not be chosen twice.";
2144   $langs{tip429} .= "\n---***---\n";
2145   $langs{tip429} .= "When playing, the filters can be used considering the straight or random order. ";
2146
2147   $langs{tip501}  = "All tabs, messages, names and tips used by gnormalize.";
2148   $langs{tip502}  = "You need to restart the Gnormalize.";
2149   $langs{tip503}  = "Guidance Language only used to help the translations.\nRead Only.";
2150   $langs{tip504}  = "Save the translations and new languages inside the directory: $lang_dir.";
2151   $langs{tip505}  = "Improve this translation or use it as base for a new Language.";
2152
2153
2154   $langs{msg001}  = "Supported Files into the directory (recursively):";
2155   $langs{msg002}  = "Supported Files into the directory (not recursively):";
2156   $langs{msg003}  = " Choose a mp3/mp4/mpc/ogg/ape/flac/wav/cda file to normalize!";
2157   $langs{msg004}  = " Dependence not satisfied!";
2158   $langs{msg005}  = "If Same Extension, Overwrite file";
2159   $langs{msg006}  = "Analyzing with ReplayGain normalization algorithms ...";
2160
2161   $langs{msg012} = ": read only!";
2162   $langs{msg013} = " Select at least one track to rip!";
2163   $langs{msg014} = " Success! Rip finished.";
2164   $langs{msg015} = " Rip stopped!";
2165   $langs{msg016} = " Success!";
2166   $langs{msg017} = " Success! File converted.";
2167   $langs{msg018} = " Success! File normalized.";
2168   $langs{msg019} = "stop ripping";
2169   $langs{msg020} = " Get info from ";
2170   $langs{msg021} = " Please, wait. cdparanoia is reading the cdrom ...";
2171   $langs{msg022} = " Please, select at least one track to play.";
2172   $langs{msg023} = "Can't find \"";
2173   $langs{msg024} = "\" in executable path.";
2174   $langs{msg025} = "The output can't be \"mp3\" format.";
2175   $langs{msg026} = "The output can't be \"ogg\" format.";
2176   $langs{msg027} = "The output can't be \"mpc\" format.";
2177   $langs{msg028} = "The output can't be \"ape\" format.";
2178   $langs{msg029} = "The output can't be \"flac\" format.";
2179   $langs{msg030} = "The output can't be \"mp4\" format.";
2180   $langs{msg031} = "The wav files can't be normalized.";
2181   $langs{msg032} = "The input can't be \"ogg\" format.";
2182   $langs{msg033} = "The input can't be \"mpc\" format.";
2183   $langs{msg034} = "The input can't be \"mp4\" format.";
2184   $langs{msg035} = "cdcd is a Command Driven CD player.";
2185   $langs{msg036} = "First run \"cdcd\" in command line to make the config file .cdcdrc";
2186   $langs{msg037} = "Choose \"n\" for the question:  Are you connected to a network (y/n) [y]?";
2187   $langs{msg038} = "Can't use the perl module \"";
2188   $langs{msg039} = "Can't play audio CD.";
2189   $langs{msg040}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
2190   $langs{msg040} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
2191   $langs{msg040} .= "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. gnormalize can convert ";
2192   $langs{msg040} .= "audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
2193   $langs{msg041}  = "This file will not be normalized!";
2194   $langs{msg042} = "According to insensibility, not normalize";
2195   $langs{msg043} = "Not normalize because adjustment ";
2196   $langs{msg044} = " is less than insensibility value.";
2197   $langs{msg045} = " not normalize ";
2198   $langs{msg046} = "Don't need encode";
2199   $langs{msg047} = " already normalized ";
2200   $langs{msg048} = "This file is already normalized.";
2201   $langs{msg049} = " Stop normalizing!";
2202   $langs{msg050} = "Applying adjustment of ";
2203   $langs{msg051} = " normalizing: adjustment of ";
2204   $langs{msg052} = "Computing levels...";
2205   $langs{msg053} = " normalized with adjustment of ";
2206   $langs{msg054} = " ripping audio to wav";
2207   $langs{msg055} = " Stop ripping!";
2208   $langs{msg056} = "copy maked.";
2209   $langs{msg057} = " Stop lame decoding!";
2210   $langs{msg058} = " Stop lame encoding!";
2211   $langs{msg061} = " This informations will be saved on normalized file.";
2212   $langs{msg062} = " tag saved.";
2213   $langs{msg063} = "Select a mp3/mp4/mpc/ogg/ape/flac/wav file.";
2214   $langs{msg064} = "Homepage: $HOMEPAGE\nThis software is available under GNU General Public Licence.";
2215   $langs{msg065} = " output: ";
2216   $langs{msg066} = "file selected - $langs{all_tab03} can be edited.";
2217   $langs{msg067} = " Unable to open cdrom drive ";
2218   $langs{msg068} = " Getting information from cddb ...";
2219   $langs{msg069} = " none cddb information.";
2220   $langs{msg070} = " Press save button on \"$langs{all_tab03}\" tab to save this changes.";
2221   $langs{msg071} = " Not connected, using ";
2222   $langs{msg072} = " cddb: can't connect to internet!";
2223   $langs{msg073} = " Can't rip audio CD! cdparanoia not found.";
2224   $langs{msg074} = " Can't rip audio CD! cdda2wav not found.";
2225   $langs{msg075} = "Select a mp3, mp4, mpc, ogg, ape, flac, or wav file";
2226   $langs{msg077} = "settings";
2227   $langs{msg078} = "Selected file";
2228   $langs{msg079} = "Recomended minimum version:";
2229   $langs{msg080} = " Invalid option!";
2230   $langs{msg081} = " Press the \"Refresh the cdrom\" button and type again the changes.";
2231   $langs{msg082} = "Not found CDDB_get perl module in your system.\n gnormalize will uses internal CDDB_get (version 2.27).";
2232   $langs{msg083} = "Not found MP3::Info perl module in your system.\n gnormalize will uses internal MP3::Info (version 1.20).";
2233   $langs{msg084}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
2234   $langs{msg084} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
2235   $langs{msg085}  = "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. ";
2236   $langs{msg085} .= "gnormalize can convert audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
2237   $langs{msg086} = "Selected Directory (recursively)";
2238   $langs{msg087} = "Select a directory containing mp3, mp4, mpc, ogg, ape, flac, or wav file.";
2239   $langs{msg088} = "Selected Directory (not recursively)";
2240   $langs{msg089} = "Removing temporary file:";
2241   $langs{msg090} = "Can not find no supported file inside of directory!";
2242   $langs{msg092} = "remove this music from list";
2243   $langs{msg093} = "Author: $AUTHOR\nEmail: $EMAIL\nDate: $DATE ; S�o Paulo - Brasil";
2244   $langs{msg094}  = "Traducci�n al Espag�ol: Victor Hugo Vidal conjuntamente con Roger Gabriels - Antwerp Belgium\n";
2245   $langs{msg094} .= "E-mail: normaliseer\@pcfreesoft.com | Homepage: http://www.pcfreesoft.com";
2246   $langs{msg095} = "You must add new language name to translate!";
2247   }
2248
2249   if ( $language eq 'Deutsch' )
2250   {
2251   $langs{all_tab01} = "Daten";
2252   $langs{all_tab02} = "Konfiguration";
2253   $langs{all_tab03} = "Information";
2254   $langs{all_tab04} = "Rip/Play";
2255   $langs{all_tab05} = "Translations";
2256   $langs{all_tab06} = "�ber";
2257   $langs{all_tab07} = "Option1";
2258   $langs{all_tab08} = "Option2";
2259   $langs{all_tab09} = "CDDB";
2260   $langs{all_tab10} = "Ripper";
2261   $langs{all_tab11} = "Colors";
2262   $langs{all_tab12} = "Players";
2263   $langs{all_tab13} = "Columns";
2264   $langs{all_tab14} = "Skins";
2265
2266   $langs{name001} = "Normungstyp";
2267   $langs{name002} = "Unempf�nglichkeit";
2268   $langs{name003} = "Selektierte Datei";
2269   $langs{name004} = "Importieren von Verzeichnis";
2270   $langs{name005} = "Output: Lossy Compression";
2271   $langs{name006} = "Output: Lossless Compression";
2272   $langs{name106} = "Output: Uncompressed";
2273
2274   $langs{name007} = "Datei";
2275   $langs{name008} = "Zur�ckkehrend";
2276   $langs{name009} = "Delete Wav Files";
2277   $langs{name010} = "Change Properties";
2278   $langs{name011} = "Overwrite";
2279   $langs{name012} = "Priority";
2280   $langs{name013} = "Add Command";
2281   $langs{name014} = "Filename Rip";
2282   $langs{name015} = "Character";
2283   $langs{name016} = "Idiom";
2284   $langs{name017} = "Font";
2285   $langs{name018} = "Read CDDB";
2286   $langs{name019} = "Overwrite CDDB";
2287   $langs{name020} = "CDDB Server";
2288   $langs{name021} = "Port";
2289   $langs{name022} = "Transport";
2290   $langs{name023} = "disable paranoia";
2291   $langs{name024} = "disable extra paranoia";
2292   $langs{name025} = "abort on skip";
2293   $langs{name026} = "never skip repair";
2294   $langs{name027} = "paranoia no-verify";
2295   $langs{name028} = "paranoia disable";
2296   $langs{name029} = "ripper";
2297   $langs{name030} = "Encode Type";
2298   $langs{name031} = "Mode";
2299   $langs{name032} = "Encode Quality";
2300   $langs{name033} = "Variable";
2301   $langs{name034} = "Title";
2302   $langs{name035} = "Artist";
2303   $langs{name036} = "Album";
2304   $langs{name037} = "Comment";
2305   $langs{name038} = "Year";
2306   $langs{name039} = "Track Number";
2307   $langs{name040} = "Genre";
2308   $langs{name041} = "Frequency";
2309   $langs{name042} = "Time";
2310   $langs{name043} = "Channels";
2311   $langs{name044} = "Length";
2312   $langs{name045} = "Track";
2313   $langs{name046} = "Artist";
2314   $langs{name047} = "Title";
2315   $langs{name048} = "Size";
2316   $langs{name049} = "Year";
2317   $langs{name050} = "Filepath";
2318   $langs{name051} = "Extension";
2319   $langs{name052} = "Exportieren von Verzeichnis";
2320   $langs{name053} = "Album";
2321   $langs{name054} = "Play Count";
2322   $langs{name055} = "Animation";
2323   $langs{name056} = "Filename";
2324   $langs{name057} = "Show Tooltips";
2325   $langs{name058} = "Guidance Language";
2326   $langs{name059} = "Current Translation";
2327   $langs{name060} = "Improve The Currente Language Translation";
2328   $langs{name061} = "Add New Language";
2329   $langs{name062} = "Add new language here and then press save buton.";
2330   $langs{name063} = "Normalisieren";
2331   $langs{name064} = "CD Player";
2332   $langs{name065} = "Play";
2333
2334   $langs{name429} = 'Play Tracks Without Any Filters';
2335   $langs{name430} = 'Play Tracks From Different Albums';
2336   $langs{name431} = 'Play Tracks From Different Artists';
2337   $langs{name432} = 'Play Tracks From Different Genres';
2338   $langs{name433} = 'Play Tracks From Different Years';
2339
2340   $langs{tip101}  = "Track: use ReplayGain Single Track gain setting.\nAlbum: don't implemented yet!\n";
2341   $langs{tip101} .= "None: no normalization is done.\n";
2342   $langs{tip101} .= "---*---\n";
2343   $langs{tip101} .= "gnormalize uses the wavegain to normalize wave files.\n";
2344   $langs{tip101} .= "wavegain is a program that applies ReplayGain normalization algorithms directly to wave files.";
2345   $langs{tip102}  = "Apply additional Manual Gain adjustment in decibels between -12.0 and +12.0. ";
2346   $langs{tip102} .= "Gain = 0.00dB correspond to 89dB standard or reference value.\n";
2347   $langs{tip102} .= "---*---\n";
2348   $langs{tip102} .= "Basic ideas of ReplayGain normalization algorithms:\n";
2349   $langs{tip102} .= "1. Calculate the Root Mean Square (RMS) of the waveform every 50ms;\n";
2350   $langs{tip102} .= "2. Sort the RMS values into numerical order, and choose the value 95% up the list to represent the overall perceived loudness;\n";
2351   $langs{tip102} .= "3. Calibrate the energy of a digital signal to 89dB standard value and apply the gain adjustment.\n";
2352   $langs{tip102} .= "---*---\n";
2353   $langs{tip102} .= "RMS(A) = sqrt(mean(A^2)) is the Root Mean Square of sound amplitude A.\n";
2354   $langs{tip102} .= "Gain(A) = 2*10*log(A/A0), such that A is the measured amplitude and A0 is the reference amplitude.";
2355   $langs{tip103}  = "Insensibility of normalize. Only not re-encode if:\n";
2356   $langs{tip103} .= "1) the extension of input file is the same of output file;\n";
2357   $langs{tip103} .= "2) and \"$langs{name010}\" button is not active;\n";
2358   $langs{tip103} .= "3) and adjust of normalize is less than insensibility value.";
2359   $langs{tip104}  = "Select one directory with supported file.";
2360   $langs{tip105}  = "Choose the directory where all the files will be saved.";
2361   $langs{tip106}  = "Normalize/Convert all supported files inside the directory (recursively or not).";
2362   $langs{tip107}  = "Select a directory including subdirectories: descende recursively into the directory preserving the same structure.";
2363   $langs{tip108}  = "After normalize, the output format will be mp3. Play it with mpg123, mpg321 or madplay.";
2364   $langs{tip109}  = "After normalize, the output format will be mp4 (also know as m4a) or aac. Play it with mplayer.";
2365   $langs{tip110}  = "After normalize, the output format will be mpc (also know as mpp or mp+). Play it with mppdec or xmms plugin.";
2366   $langs{tip111}  = "After normalize, the output format will be ogg. Play it with ogg123.";
2367   $langs{tip112}  = "After normalize, the output format will be ape. Play it with the command:\n   mac \"song.ape\"  - -d | play -t wav - ";
2368   $langs{tip113}  = "After normalize, the output format will be flac. Play it with flac123 or xmms plugin.\nRequired flac version >= \"1.1.1\".";
2369   $langs{tip114}  = "Es ist geeignet, Audio-Cds von wav-Dateien herzustellen.\nWav ist ein unkrompimiertes Audio Format.";
2370   $langs{tip115}  = "NOTE:\n";
2371   $langs{tip115} .= "This compression technique is known as 'Lossy Compression' whereby during the compression data can be lost. ";
2372   $langs{tip115} .= "The degree of loss varies upon settings. Although data loss does occur, it is minimal and certainly not noticable ";
2373   $langs{tip115} .= "when using good settings.\nExamples of Lossy compression audio: Mp3, Mp4, Mpc and Ogg.";
2374   $langs{tip116}  = "NOTE:\n";
2375   $langs{tip116} .= "This compression technique is known as 'Lossless Compression' in which all of the information in the original ";
2376   $langs{tip116} .= "data is preserved, and the original data may be recovered in form identical to its original form.\n";
2377   $langs{tip116} .= "Examples of Lossless compression audio: Ape and Flac.";
2378   $langs{tip117}  = "Show the progress bar.";
2379   $langs{tip118}  = "Hide the progress bar.";
2380   $langs{tip119}  = "Show the command line and others info.";
2381   $langs{tip120}  = "Hide the command line and others info.";
2382   $langs{tip121}  = "Quit.";
2383   $langs{tip122}  = "Clear all messages.";
2384
2385   $langs{tip201}  = "After the normalize process, the temporary wav files will be deleted.";
2386   $langs{tip202}  = "After normalize the files, you can change its encode properties by determining the encode type, the bitrate of all ";
2387   $langs{tip202} .= "output files, etc. Otherwise, the bitrate of each input file and others properties will be conserved.";
2388   $langs{tip203}  = "The normalized file will be saved in the same directory of input. If it already exist then overwrite the original file.";
2389   $langs{tip204}  = "Show the hints and informations.";
2390   $langs{tip205}  = "Show the Tux and others penguins animations.";
2391   $langs{tip206}  = "Modify scheduling priority: 0 (highest priority) to 19 (lowest)";
2392   $langs{tip207}  = "Output filename format used only for rip Audio CD:\n";
2393   $langs{tip207} .= "   %a - artist\n";
2394   $langs{tip207} .= "   %b - album\n";
2395   $langs{tip207} .= "   %t - song title\n";
2396   $langs{tip207} .= "   %n - track number\n";
2397   $langs{tip207} .= "   %y - year\n";
2398   $langs{tip207} .= "   %g - genre";
2399   $langs{tip208}  = "Advised font: Sans 10.";
2400   $langs{tip209}  = "Read the CDDB info for Audio CD in your drive from CDDB server \"$CDDB_server\" or from files saved into $home/.cddd/.";
2401   $langs{tip210}  = "Always get CDDB info from server and overwrite the CDDB file saved at $home/.cddd/";
2402   $langs{tip211}  = "Some servers:\nfreedb.freedb.org:8880\nus.freedb.org:8880\ncddb.com\nus.cddb.com:8880\nsunsite.unc.edu:8880";
2403   $langs{tip212}  = "Choose the port number.";
2404   $langs{tip213}  = "Disable all data verification and correction features. Consequently the extraction/rip is more fast. ";
2405   $langs{tip213} .= "This option implies that -Y is active. See cdparanoia (option -Z).";
2406   $langs{tip214}  = "Do not accept any skips; retry forever if needed. See cdparanoia (option -z).";
2407   $langs{tip215}  = "Disables intra-read data verification; only overlap checking at read boundaries is performed. Not recommended. See cdparanoia (option -Y).\n";
2408   $langs{tip215} .= "Boundaries are spaces/limit inter tracks of an audio cd. cdparanoia reads this boundaries carefully when rip on track.";
2409   $langs{tip216}  = "If the read skips due to imperfect data, a scratch, whatever, abort reading this track. See cdparanoia (option -X).";
2410   $langs{tip217}  = "Use the paranoia library instead of cdda2wav's routines for reading. See cdda2wav (option -paranoia).";
2411   $langs{tip218}  = "Disables paranoia mode. Paranoia is still being used. See cdda2wav (option -paraopts=help).";
2412   $langs{tip219}  = "Switches verify off, and overlap on.";
2413   $langs{tip220}  = "Choose a targeted average bitrate. Sets encoding to the bitrate closest to this value (in Kb/s).";
2414   $langs{tip221}  = "Adjust the quality of mp3; 9 is the worst quality, 0 is the best quality with more Kb/s. See: man lame (option: -V).";
2415   $langs{tip222}  = "Adjust the quality of encode for ogg; 10 is the best quality with more Kb/s used, -1 is the worst quality. See: man oggenc.";
2416   $langs{tip223}  = "Adjust the quality of lame encoder for mp3 files: 0 is the best quality with maximum ";
2417   $langs{tip223} .= "algorithms process; 9 is the worst quality but more fast. See: man lame (option: -q).";
2418   $langs{tip224}  = "Set default variable bitrate (VBR) quantizer quality in percent for mp4 format; 500 is the best ";
2419   $langs{tip224} .= "quality with more Kb/s used; 10 is the worst quality. See: <faac --help>.";
2420   $langs{tip225}  =  "Adjust the quality of encode for mpc using profiles; 8 (braindead) is the best quality with more Kb/s used,";
2421   $langs{tip225} .= " 2 (telephone) is the worst quality. See: mppenc --longhelp\n";
2422   $langs{tip225} .= " q=2 --telephone - lowest quality,       (32... 48 kbps)\n";
2423   $langs{tip225} .= " q=3 --thumb     - low quality/internet, (58... 86 kbps)\n";
2424   $langs{tip225} .= " q=4 --radio     - medium (MP3) quality, (112...152 kbps)\n";
2425   $langs{tip225} .= " q=5 --standard  - high quality (dflt),  (142...184 kbps)\n";
2426   $langs{tip225} .= " q=6 --xtreme    - extreme high quality, (168...212 kbps)\n";
2427   $langs{tip225} .= " q=7 --insane    - extreme high quality, (232...268 kbps)\n";
2428   $langs{tip225} .= " q=8 --braindead - extreme high quality, (232...278 kbps)";
2429   $langs{tip226}  = "Compression level:\n  fast: 1000\n  normal: 2000\n  high: 3000\n  extra high: 4000\n  insane: 5000\n";
2430   $langs{tip226} .= "\nMore compression level \nresults small files.\nSee the help: mac";
2431   $langs{tip227}  = "Compression level:\n  fast: 0\n  normal: 5\n  best: 8\nSee <man flac>.";
2432   $langs{tip228}  = "Put here the additional command line used to encode this files.";
2433   $langs{tip229}  = "Used to encode ID3v2.3.0 Tag for MP3 files.";
2434   $langs{tip250}  = "If this entry is empty, the original comment tag will be preserved. ";
2435   $langs{tip250} .= "The default comment is \"gnormalize - gain=%gain\" with %gain replaced by its value.";
2436
2437   $langs{tip301}  = "If there is more than one artist, use:\nArtist1 & Artist2 & Artist3 & ...";
2438   $langs{tip302}  = "Track number";
2439   $langs{tip303}  = "Total track number";
2440   $langs{tip304}  = "Save changes of tag.";
2441   $langs{tip305}  = "Save changes of tag to $home/.cddb directory.";
2442
2443   $langs{tip401}  = "Try /dev/cdrom, /dev/dvd, /dev/hdc or ...\nSee \"/etc/fstab\" file.";
2444   $langs{tip402}  = "Refresh the cdrom drive and erase the playlist.";
2445   $langs{tip403}  = "Unselect all tracks.";
2446   $langs{tip404}  = "Select all tracks.";
2447   $langs{tip405}  = "Add more files to play.";
2448   $langs{tip406}  = "Close the gnormalize player config.";
2449   $langs{tip407}  = "Choose the interval in milliseconds that the text will be redrawed.";
2450   $langs{tip408}  = "Choose the step incremental in pixels.";
2451   $langs{tip409}  = "Scrolling text orientation: up/down or left/right.";
2452   $langs{tip410}  = "Show the xTunes like skin.\nTo take effect, change the display mode.";
2453   $langs{tip411}  = "Always On Top: keep the play mode window above the others windows.\nTo take effect, change the display mode.";
2454   $langs{tip429}  = "Play tracks that satisfies established filters can be more convenient or pleasant than the general random approach. ";
2455   $langs{tip429} .= "The available filters are 'Play Tracks From Different Albums/Artists/Genres/Years'.";
2456   $langs{tip429} .= "\n---***---\n";
2457   $langs{tip429} .= "For example, if the filter is set to Albums, then play tracks jumping between different Albums. ";
2458   $langs{tip429} .= "In this case, all Albums will be played before repeat some Album. ";
2459   $langs{tip429} .= "In others words, until one track has been played from each Album, the same Album will not be chosen twice.";
2460   $langs{tip429} .= "\n---***---\n";
2461   $langs{tip429} .= "When playing, the filters can be used considering the straight or random order. ";
2462
2463   $langs{tip501}  = "All tabs, messages, names and tips used by gnormalize.";
2464   $langs{tip502}  = "Sie m�ssen den Gnormalize wiederanfangen.";
2465   $langs{tip503}  = "Guidance Language only used to help the translations.\nRead Only.";
2466   $langs{tip504}  = "Save the translations and new languages inside the directory: $lang_dir.";
2467   $langs{tip505}  = "Improve this translation or use it as base for a new Language.";
2468
2469
2470   $langs{msg001}  = "Supported Files into the directory (recursively):";
2471   $langs{msg002}  = "Supported Files into the directory (not recursively):";
2472   $langs{msg003}  = " Choose a mp3/mp4/mpc/ogg/ape/flac/wav/cda file to normalize!";
2473   $langs{msg004}  = " Dependence not satisfied!";
2474   $langs{msg005}  = "If Same Extension, Overwrite file";
2475   $langs{msg006}  = "Analyzing with ReplayGain normalization algorithms ...";
2476
2477   $langs{msg012} = ": read only!";
2478   $langs{msg013} = " Select at least one track to rip!";
2479   $langs{msg014} = " Success! Rip finished.";
2480   $langs{msg015} = " Rip stopped!";
2481   $langs{msg016} = " Success!";
2482   $langs{msg017} = " Success! File converted.";
2483   $langs{msg018} = " Success! File normalized.";
2484   $langs{msg019} = "stop ripping";
2485   $langs{msg020} = " Get info from ";
2486   $langs{msg021} = " Please, wait. cdparanoia is reading the cdrom ...";
2487   $langs{msg022} = " Please, select at least one track to play.";
2488   $langs{msg023} = "Can't find \"";
2489   $langs{msg024} = "\" in executable path.";
2490   $langs{msg025} = "The output can't be \"mp3\" format.";
2491   $langs{msg026} = "The output can't be \"ogg\" format.";
2492   $langs{msg027} = "The output can't be \"mpc\" format.";
2493   $langs{msg028} = "The output can't be \"ape\" format.";
2494   $langs{msg029} = "The output can't be \"flac\" format.";
2495   $langs{msg030} = "The output can't be \"mp4\" format.";
2496   $langs{msg031} = "The wav files can't be normalized.";
2497   $langs{msg032} = "The input can't be \"ogg\" format.";
2498   $langs{msg033} = "The input can't be \"mpc\" format.";
2499   $langs{msg034} = "The input can't be \"mp4\" format.";
2500   $langs{msg035} = "cdcd is a Command Driven CD player.";
2501   $langs{msg036} = "First run \"cdcd\" in command line to make the config file .cdcdrc";
2502   $langs{msg037} = "Choose \"n\" for the question:  Are you connected to a network (y/n) [y]?";
2503   $langs{msg038} = "Can't use the perl module \"";
2504   $langs{msg039} = "Can't play audio CD.";
2505   $langs{msg040}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
2506   $langs{msg040} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
2507   $langs{msg040} .= "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. gnormalize can convert ";
2508   $langs{msg040} .= "audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
2509   $langs{msg041}  = "This file will not be normalized!";
2510   $langs{msg042} = "According to insensibility, not normalize";
2511   $langs{msg043} = "Not normalize because adjustment ";
2512   $langs{msg044} = " is less than insensibility value.";
2513   $langs{msg045} = " not normalize ";
2514   $langs{msg046} = "Don't need encode";
2515   $langs{msg047} = " already normalized ";
2516   $langs{msg048} = "This file is already normalized.";
2517   $langs{msg049} = " Stop normalizing!";
2518   $langs{msg050} = "Applying adjustment of ";
2519   $langs{msg051} = " normalizing: adjustment of ";
2520   $langs{msg052} = "Computing levels...";
2521   $langs{msg053} = " normalized with adjustment of ";
2522   $langs{msg054} = " ripping audio to wav";
2523   $langs{msg055} = " Stop ripping!";
2524   $langs{msg056} = "copy maked.";
2525   $langs{msg057} = " Stop lame decoding!";
2526   $langs{msg058} = " Stop lame encoding!";
2527   $langs{msg061} = " This informations will be saved on normalized file.";
2528   $langs{msg062} = " tag saved.";
2529   $langs{msg063} = "Select a mp3/mp4/mpc/ogg/ape/flac/wav file.";
2530   $langs{msg064} = "Homepage: $HOMEPAGE\nThis software is available under GNU General Public Licence.";
2531   $langs{msg065} = " output: ";
2532   $langs{msg066} = "file selected - $langs{all_tab03} can be edited.";
2533   $langs{msg067} = " Unable to open cdrom drive ";
2534   $langs{msg068} = " Getting information from cddb ...";
2535   $langs{msg069} = " none cddb information.";
2536   $langs{msg070} = " Press save button on \"$langs{all_tab03}\" tab to save this changes.";
2537   $langs{msg071} = " Not connected, using ";
2538   $langs{msg072} = " cddb: can't connect to internet!";
2539   $langs{msg073} = " Can't rip audio CD! cdparanoia not found.";
2540   $langs{msg074} = " Can't rip audio CD! cdda2wav not found.";
2541   $langs{msg075} = "Select a mp3, mp4, mpc, ogg, ape, flac, or wav file";
2542   $langs{msg077} = "settings";
2543   $langs{msg078} = "Selected file";
2544   $langs{msg079} = "Recomended minimum version:";
2545   $langs{msg080} = " Invalid option!";
2546   $langs{msg081} = " Press the \"Refresh the cdrom\" button and type again the changes.";
2547   $langs{msg082} = "Not found CDDB_get perl module in your system.\n gnormalize will uses internal CDDB_get (version 2.27).";
2548   $langs{msg083} = "Not found MP3::Info perl module in your system.\n gnormalize will uses internal MP3::Info (version 1.20).";
2549   $langs{msg084}  = "gnormalize is an audio converter and CD ripper with ReplayGain normalization algorithms, an audio player and a ";
2550   $langs{msg084} .= "metadata (tag) editor. It uses 'wavegain' to normalize wave files with the accurate gain_analysis.c code from ReplayGain. ";
2551   $langs{msg085}  = "gnormalize decodes the file to WAV format, then normalizes the WAV and re-encodes it. ";
2552   $langs{msg085} .= "gnormalize can convert audio format between MP3, MP4, MPC, OGG, APE, FLAC, and WAV.";
2553   $langs{msg086} = "Selected Directory (recursively)";
2554   $langs{msg087} = "Select a directory containing mp3, mp4, mpc, ogg, ape, flac, or wav file.";
2555   $langs{msg088} = "Selected Directory (not recursively)";
2556   $langs{msg089} = "Removing temporary file:";
2557   $langs{msg090} = "Can not find no supported file inside of directory!";
2558   $langs{msg092} = "remove this music from list";
2559   $langs{msg093} = "Author: $AUTHOR\nEmail: $EMAIL\nDate: $DATE ; S�o Paulo - Brasil";
2560   $langs{msg094}  = "Deutsche �bersetzung gemacht durch Roger Gabriels Antwerp (Essen) Belgium\n";
2561   $langs{msg094} .= "E-mail: normaliseer\@pcfreesoft.com | Homepage: htp://www.pcfreesoft.com";
2562   $langs{msg095} = "You must add new language name to translate!";
2563   }
2564
2565   #if ( $language eq 'new_language' )
2566   #{
2567   # ...
2568   # ...
2569   #}
2570
2571   read_new_langs();
2572   foreach my $lang ( sort keys %all_language ) { read_langs("$lang_dir/${lang}.txt") if ( $language eq $lang ); }
2573
2574} # final of 'sub set_language'
2575
2576set_language();
2577
2578sub read_new_langs { # read new language inside $lang_dir
2579   return if ( ! -d filename_from_unicode $lang_dir );
2580   opendir(DIR, filename_from_unicode $lang_dir ) ;  #search for $lang_dir directory
2581      while (my $file = readdir(DIR)) {
2582         next if $file eq ".";
2583         next if $file eq "..";
2584         if ( -w filename_from_unicode "$lang_dir/$file" ){ # -w  File is writable by effective uid/gid.
2585            ( my $lang = $file) =~ s/\.txt$//;
2586	    #print "\$file = $file ; \$lang = $lang \n";
2587	    $all_language{$lang} = $true; # Set New Language
2588         }
2589   }
2590   closedir(DIR);
2591}
2592
2593sub read_langs {
2594   my $file = shift;
2595   return if ( ! -e filename_from_unicode $file );
2596   my $last_key = 'msg001';
2597   open( INPUT, '<:utf8', filename_from_unicode $file ) or die "Can't open <$file>: $!, stopped";
2598   while (my $line = <INPUT>) {  # regular expressions: see 'man perlre' and 'man perlretut'.
2599      chomp($line);              # avoid \n on last field
2600      next if ( $line =~ /^$/ ); # remover linha vazia   # ^$ matches an empty string
2601      #$line =~ s/\n/\\n/sg; print "\$line =  $line\n";
2602      my $key; my $value;
2603
2604      #if ( $line =~ /^\$langs{(.*)}\s+=\s+"(.*)";/s){ $key = $1; $value = $2 }   # $langs{$key}=$value
2605      if ( $line =~ /^(.*)<===>(.*)/s){ $key = $1; $value = $2 }                  # $langs{$key}=$value
2606
2607      #print "\$key = $key ; \$last_key = $last_key \n";
2608      if ( not defined $key and defined $last_key and defined $line ) { $langs{$last_key} .= "\n$line"; next; }
2609
2610      $langs{$key} = $value;
2611      $last_key = $key;
2612   }
2613   close( INPUT );
2614   #print "333-->\$language = $language \n";
2615}
2616
2617#############----------Main--Window----------###############
2618#####--------------------------------------------------#####
2619#######-------------------Initial-------------------########
2620
2621# main window - tela principal
2622$window = new Gtk2::Window( "toplevel" );  # principal janela
2623$window->signal_connect( "delete_event", \&Quit );
2624$window->set_border_width( 1 );
2625$window->set_decorated ($true);
2626# Realization is necessary when you want a widget to use
2627# a GdkWindow to access the X server.
2628$window->realize;
2629$window->set( 'resizable' => $true, 'allow-shrink' => $false, 'allow-grow' => $true);
2630# for set_uposition see <man Gtk2::Widget>
2631#$window->set_uposition (300,200); # (x,y) - window should be placed on the screen.
2632# 'center'; 'none'; 'mouse' ; 'center-on-parent', 'center-always' ;
2633#$window->set_position ('center-always');
2634$window->set_title( "gnormalize - $VERSION" );
2635#Gtk2::Widget->set_size_request($widget, $width, $height);
2636#$window->set_size_request(520,450); #minimum required size, can't shrink for size less than this.
2637$window->resize($window_width, $window_height);
2638#my $color = Gtk2::Gdk::Color->parse ("#B8D3C1"); $window->modify_bg ('normal', $color);
2639$window->show;
2640
2641###------------ vbox main -----------------###
2642# "Inside the window we need a vbox to arrange the widgets vertically
2643# $vbox = new Gtk2::VBox( $homogeneous, $spacing );
2644# $vbox->pack_start( $child, $expand, $fill, $padding );
2645# If the $expand  argument is a true value, then the widgets are laid out
2646# in the box to fill in all the extra space in the box so the box is expanded
2647# to fill the area allotted to it; otherwise the box is shrunk to just fit
2648# the widgets. Setting expand to a false value will allow you to do right
2649# and left justification of your widgets. Note that setting $homogeneous
2650# to true on the VBox is the same as setting $expand to true for each widget."
2651
2652my $vbox_main = new Gtk2::VBox( $false, 0 );
2653$vbox_main->set_border_width( 0 );
2654$window->add($vbox_main);
2655$vbox_main->show;
2656
2657###-----------------vpaned-----------------###
2658###-----------------init-------------------###
2659# see <man Gtk2::Paned>
2660# Inside the $vbox_main we put vpaned
2661# and inside vpaned we put vbox_up and vbox_down
2662
2663my $vpaned = Gtk2::VPaned->new;
2664$vpaned->set_border_width (0);
2665$vbox_main->pack_start( $vpaned, $true, $true, 0 );
2666$vpaned->show;
2667
2668###--------------- vbox_up --------------------###
2669
2670$vbox_up = new Gtk2::VBox( $false, 2 );
2671$vbox_up->set_border_width( 1 );
2672$vpaned->add1 ($vbox_up);
2673$vbox_up->show;
2674
2675###------------ vbox_down ------------------###
2676
2677my $vbox_down = new Gtk2::VBox( $false, 2 );
2678$vbox_down->set_border_width( 1 );
2679$vpaned->add2 ($vbox_down);
2680$vbox_down->show;
2681
2682###-----------------------------------------###
2683
2684# "child1_resize" determines whether the first child
2685# should expand when $paned is resized.
2686$vpaned->child1_resize ($true);
2687$vpaned->child2_resize ($true);
2688
2689# "If shrink is true, then that child can be made
2690# smaller than its requisition by the user."
2691$vpaned->child1_shrink($false);
2692$vpaned->child2_shrink($false);
2693
2694###-----------------final------------------###
2695###--------------- vpaned -----------------###
2696
2697
2698#------------------ tabs for notebook ---------------#
2699
2700#criamos o vbox1 e o adicionamos dentro do notebook
2701$vbox1 = new Gtk2::VBox( $false, 2 );
2702$vbox1->set_border_width( 4 );
2703$vbox1->show();
2704
2705#criamos o vbox2 e o adicionamos dentro do notebook
2706$vbox2 = new Gtk2::VBox( $false, 2 );
2707$vbox2->set_border_width( 4 );
2708$vbox2->show();
2709
2710#criamos o vbox3 e o adicionamos dentro do notebook
2711my $vbox3 = new Gtk2::VBox( $false, 2 );
2712$vbox3->set_border_width( 4 );
2713$vbox3->show();
2714
2715#criamos o vbox4 e o adicionamos dentro do notebook
2716my $vbox4 = new Gtk2::VBox( $false, 2 );
2717$vbox4->set_border_width( 4 );
2718$vbox4->show();
2719
2720#criamos o vbox5 e o adicionamos dentro do notebook
2721my $vbox5 = new Gtk2::VBox( $false, 2 );
2722$vbox5->set_border_width( 4 );
2723$vbox5->show();
2724
2725#criamos o vbox6 e o adicionamos dentro do notebook
2726my $vbox6 = new Gtk2::VBox( $false, 2 );
2727$vbox6->set_border_width( 4 );
2728$vbox6->show();
2729
2730#------------------ icons for notebook ---------------#
2731
2732sub DrawIcons {  # To show the icons correctely
2733   my ($stockid,$size) = @_;
2734   my $icon_image = Gtk2::Image->new_from_stock($stockid,$size);
2735   $icon_image->show;
2736   return $icon_image;
2737}
2738
2739# widget = Gtk2::Image->new_from_stock ($stock_id, $size)
2740# $stock_id: see <man Gtk2::Stock>
2741# size : menu, small-toolbar, large-toolbar, button, dnd, dialog
2742my $tab_icon_size = 'small-toolbar';
2743my $label_data = Gtk2::Label->new($langs{all_tab01});
2744$label_data->show;
2745my $vbox_tab_data = Gtk2::VBox->new( $false, 1 );
2746$vbox_tab_data->add( DrawIcons('gtk-harddisk', $tab_icon_size) );
2747$vbox_tab_data->add($label_data);
2748$vbox_tab_data->show;
2749
2750#my $notebook_color = Gtk2::Gdk::Color->parse ("blue"); # #B8D3C1 = 184 211 193 (RGB)
2751#$label_data->modify_fg ('normal', $notebook_color);
2752
2753# for config notebook icon
2754my $label_config = Gtk2::Label->new($langs{all_tab02});
2755$label_config->show;
2756my $vbox_tab_config = Gtk2::VBox->new( $false, 1 );
2757$vbox_tab_config->add(DrawIcons('gtk-preferences', $tab_icon_size));
2758$vbox_tab_config->add($label_config);
2759$vbox_tab_config->show;
2760
2761# for info notebook icon
2762my $label_info = Gtk2::Label->new($langs{all_tab03});
2763$label_info->show;
2764my $vbox_tab_info = Gtk2::VBox->new( $false, 1 );
2765$vbox_tab_info->add(DrawIcons('gtk-find-and-replace', $tab_icon_size));
2766$vbox_tab_info->add($label_info);
2767$vbox_tab_info->show;
2768
2769# for rip notebook icon
2770my $label_rip = Gtk2::Label->new($langs{all_tab04});
2771$label_rip->show;
2772my $vbox_tab_rip = Gtk2::VBox->new( $false, 1 );
2773$vbox_tab_rip->add(DrawIcons('gtk-cdrom', $tab_icon_size));
2774$vbox_tab_rip->add($label_rip);
2775$vbox_tab_rip->show;
2776
2777# for rip notebook icon
2778my $label_transl = Gtk2::Label->new($langs{all_tab05});
2779$label_transl->show;
2780my $vbox_tab_transl = Gtk2::VBox->new( $false, 1 );
2781$vbox_tab_transl->add(DrawIcons('gtk-spell-check', $tab_icon_size));
2782$vbox_tab_transl->add($label_transl);
2783$vbox_tab_transl->show;
2784
2785# for about notebook icon
2786my $label_about = Gtk2::Label->new($langs{all_tab06});
2787$label_about->show;
2788my $vbox_tab_about = Gtk2::VBox->new( $false, 1 );
2789$vbox_tab_about->add(DrawIcons('gtk-dialog-info', $tab_icon_size));
2790$vbox_tab_about->add($label_about);
2791$vbox_tab_about->show;
2792
2793#####-------------------Notebook-----------------#######
2794# we put vbox1, vbox2, vbox3, vbox4 and vbox6 inside notebook
2795# and we put notebook inside vbox_up
2796
2797$notebook = Gtk2::Notebook->new;
2798$notebook->set( 'homogeneous' => $true, 'scrollable' => $true );
2799
2800#$notebook->append_page_menu ($child, $tab_label, $menu_label)
2801$notebook->append_page_menu ( $vbox1,  $vbox_tab_data, undef );    # data
2802$notebook->append_page_menu ( $vbox2,  $vbox_tab_config, undef );  # config
2803$notebook->append_page_menu ( $vbox3,  $vbox_tab_info, undef );    # info
2804$notebook->append_page_menu ( $vbox4,  $vbox_tab_rip, undef );     # rip
2805$notebook->append_page_menu ( $vbox5,  $vbox_tab_transl, undef );  # Translation
2806$notebook->append_page_menu ( $vbox6,  $vbox_tab_about, undef );   # about
2807#$box->pack_start ($child, $expand, $fill, $padding)
2808$vbox_up->pack_start( $notebook, $true, $true, 0 );
2809#$notebook->signal_connect("switch-page", \&refresh_notebook_test);
2810#$vbox_up->add( $notebook );
2811$notebook->show;
2812
2813#integer = $notebook->get_current_page;
2814#$notebook->set_current_page ($notebook_page_num); # see this command on the final
2815
2816#################------ItemFactory------################
2817# Inside the vbox1 we put frame1
2818
2819###--------------frame1--------------------###
2820# Adicionamos este frame1 em vbox1
2821$frame1 = new Gtk2::Frame( $langs{name063} . ": mp3/mp4/mpc/ogg/ape/flac/wav");
2822# Set the frame's label
2823# Align the label at the right of the frame
2824$frame1->set_label_align( 1.0, 0.0 );
2825# Set the style of the frame
2826$frame1->set_shadow_type( 'etched_out' );
2827$vbox1->add($frame1);
2828$frame1->show();
2829
2830#adicionamos uma tabela dentro do frame
2831#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
2832$table1 = new Gtk2::Table (4, 6, 0);
2833$table1->set_border_width (4);
2834$table1->set_row_spacings(4);
2835$table1->set_col_spacings(4);
2836$frame1->add( $table1 );
2837$table1->show;
2838
2839# on hbox1 we put more two frames: frame11 and frame12
2840# Gtk2::HBox->new ($homogeneous=0, $spacing=5)
2841my $hbox1 = Gtk2::HBox->new ($true, 2);
2842$vbox1->add($hbox1);
2843$hbox1->show;
2844
2845###--------------frame11--------------------###
2846# Adicionamos este frame11 em hbox1
2847my $frame11 = new Gtk2::Frame($langs{name005});
2848$frame11->set_label_align( 1.0, 0.0 );
2849$frame11->set_shadow_type( 'etched_out' );
2850$hbox1->add($frame11);
2851$frame11->show();
2852
2853#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
2854my $table11 = new Gtk2::Table (2, 2, 0);
2855$table11->set_border_width (4);
2856$table11->set_row_spacings(4);
2857$table11->set_col_spacings(0);
2858$frame11->add( $table11 );
2859$table11->show;
2860
2861###--------------frame12--------------------###
2862# Adicionamos este frame12 em hbox1
2863my $frame12 = new Gtk2::Frame($langs{name006});
2864$frame12->set_label_align( 1.0, 0.0 );
2865$frame12->set_shadow_type( 'etched_out' );
2866$hbox1->add($frame12);
2867$frame12->show();
2868
2869my $table12 = new Gtk2::Table (2, 2, 0);
2870$table12->set_border_width (4);
2871$table12->set_row_spacings(6);
2872$table12->set_col_spacings(2);
2873$frame12->add( $table12 );
2874$table12->show;
2875
2876###--------------frame13--------------------###
2877
2878# Adicionamos este frame13 em hbox1
2879my $frame13 = new Gtk2::Frame($langs{name106});
2880$frame13->set_label_align( 1.0, 0.0 );
2881$frame13->set_shadow_type( 'etched_out' );
2882$hbox1->add($frame13);
2883$frame13->show();
2884
2885my $table13 = new Gtk2::Table (2, 1, 0);
2886$table13->set_border_width (4);
2887$table13->set_row_spacings(6);
2888$table13->set_col_spacings(2);
2889$frame13->add( $table13 );
2890$table13->show;
2891
2892###--------------frame2--------------------###
2893# we put frame2 inside notebook2
2894my $frame2 = new Gtk2::Frame();
2895# Align the label at the right of the frame
2896$frame2->set_label_align( 1.0, 0.0 );
2897# Set the style of the frame
2898$frame2->set_shadow_type( 'etched_out' );
2899#$vbox2->add($frame2);
2900$frame2->show();
2901
2902#adicionamos uma tabela dentro do frame
2903#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
2904my $table2 = new Gtk2::Table (3, 4, 0);
2905$table2->set_border_width (4);
2906$table2->set_row_spacings(4);
2907$table2->set_col_spacings(10);
2908$frame2->add( $table2 );
2909$table2->show;
2910
2911###--------------frame202--------------------###
2912# we put frame202 inside notebook2
2913my $frame202 = new Gtk2::Frame();
2914$frame202->set_label_align( 1.0, 0.0 );
2915$frame202->set_shadow_type( 'etched_out' );
2916$frame202->show();
2917
2918#adicionamos uma tabela dentro do frame
2919#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
2920my $table202 = new Gtk2::Table (3, 4, 0);
2921$table202->set_border_width (4);
2922$table202->set_row_spacings(4);
2923$table202->set_col_spacings(4);
2924$frame202->add( $table202 );
2925$table202->show;
2926
2927###--------------frame203--------------------###
2928# we put frame203 inside notebook2
2929my $frame203 = new Gtk2::Frame();
2930$frame203->set_label_align( 1.0, 0.0 );
2931$frame203->set_shadow_type( 'etched_out' );
2932$frame203->show();
2933
2934#adicionamos uma tabela dentro do frame
2935#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
2936my $table203 = new Gtk2::Table (3, 4, 0);
2937$table203->set_border_width (4);
2938$table203->set_row_spacings(4);
2939$table203->set_col_spacings(8);
2940$frame203->add( $table203 );
2941$table203->show;
2942
2943###--------------frame204--------------------###
2944# we put frame204 inside notebook2
2945my $frame204 = new Gtk2::Frame("cdparanoia");
2946$frame204->set_label_align( 1.0, 0.0 );
2947$frame204->set_shadow_type( 'etched_out' );
2948$frame204->show();
2949
2950#adicionamos uma tabela dentro do frame
2951#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
2952my $table204 = new Gtk2::Table (3, 4, 0);
2953$table204->set_border_width (4);
2954$table204->set_row_spacings(4);
2955$table204->set_col_spacings(8);
2956$frame204->add( $table204 );
2957$table204->show;
2958
2959###--------------frame205--------------------###
2960# we put frame205 inside notebook2
2961my $frame205 = new Gtk2::Frame("cdda2wav");
2962$frame205->set_label_align( 1.0, 0.0 );
2963$frame205->set_shadow_type( 'etched_out' );
2964$frame205->show();
2965
2966#adicionamos uma tabela dentro do frame
2967#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
2968my $table205 = new Gtk2::Table (3, 4, 0);
2969$table205->set_border_width (4);
2970$table205->set_row_spacings(4);
2971$table205->set_col_spacings(8);
2972$frame205->add( $table205 );
2973$table205->show;
2974
2975#-------------------Notebook2----------------#
2976# we put frame2 inside notebook2
2977# and we put notebook2 inside vbox2
2978$notebook2 = Gtk2::Notebook->new;
2979# position = 'left', 'right','top', 'bottom'
2980$notebook2->set( homogeneous => $true  ); # 'tab-hborder' => 20
2981$notebook2->set_tab_pos( 'left' );
2982
2983#integer = $notebook2->get_current_page;
2984#$notebook2->set_current_page ($notebook_page_num2); # see this command on the final
2985
2986my $label_option1 = Gtk2::Label->new( $langs{all_tab07} );
2987my $label_option2 = Gtk2::Label->new( $langs{all_tab08} );
2988my $label_cddb = Gtk2::Label->new( $langs{all_tab09} );
2989my $label_ripper = Gtk2::Label->new( $langs{all_tab10} );
2990#$notebook->append_page_menu ($child, $tab_label, $menu_label)
2991$notebook2->append_page_menu ( $frame2,   $label_option1, undef );    # option1
2992$notebook2->append_page_menu ( $frame202, $label_option2, undef );    # option2
2993$notebook2->append_page_menu ( $frame203, $label_cddb,    undef );    # cddb
2994if ($ripper eq "cdparanoia"){ $notebook2->append_page_menu ( $frame204, $label_ripper, undef  ); }    # ripper
2995if ($ripper eq "cdda2wav"  ){ $notebook2->append_page_menu ( $frame205, $label_ripper, undef  ); }    # ripper
2996
2997# $vbox->pack_start( $child, $expand, $fill, $padding );
2998$vbox2->pack_start( $notebook2, $true, $true, 0 );
2999#$vbox2->add( $notebook2 );
3000$notebook2->show;
3001
3002###--------------frame21--------------------###
3003# Adicionamos este frame21 em vbox2 (vbox2 est� dentro do notebook)
3004my $frame21 = new Gtk2::Frame("lame ".$langs{msg077} );
3005$frame21->set_label_align( 1.0, 0.0 );
3006$frame21->set_shadow_type( 'etched_out' );
3007# $vbox->pack_start( $child, $expand, $fill, $padding );
3008$vbox2->pack_start( $frame21, $true, $true, 0 );
3009#$vbox2->add($frame21);
3010$frame21->show();
3011
3012#adicionamos uma tabela21 dentro do frame21
3013my $table21 = new Gtk2::Table (3, 7, $false);
3014$table21->set_border_width (4);
3015$table21->set_row_spacings(4);
3016$table21->set_col_spacings(4);
3017$frame21->add( $table21 );
3018$table21->show;
3019
3020###----------------hpaned3-----------------###
3021###-----------------init-------------------###
3022# see <man Gtk2::Paned>
3023# Inside the vbox3 we put hpaned3
3024# and inside hpaned3 we put frame3 and frame31
3025
3026my $hpaned3 = Gtk2::HPaned->new;
3027$vbox3->pack_start ($hpaned3, $true, $true, 0);
3028$hpaned3->set_border_width (0);
3029$hpaned3->show;
3030
3031###--------------frame3--------------------###
3032my $frame3 = new Gtk2::Frame("Tag");
3033# Align the label at the right of the frame
3034$frame3->set_label_align( 1.0, 0.0 );
3035# Set the style of the frame
3036$frame3->set_shadow_type( 'etched_in' );
3037$hpaned3->add1 ($frame3);
3038$frame3->show();
3039
3040#adicionamos uma tabela dentro do frame
3041#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
3042my $table3 = new Gtk2::Table (6, 4, 0);
3043$table3->set_size_request( 350 ,-1 ); # width (70%), height;
3044$table3->set_border_width (4);
3045$table3->set_row_spacings(6);
3046$table3->set_col_spacings(4);
3047$frame3->add( $table3 );
3048$table3->show;
3049
3050###--------------frame31--------------------###
3051my $frame31 = new Gtk2::Frame("MP3 Info");
3052# Align the label at the right of the frame
3053$frame31->set_label_align( 1.0, 0.0 );
3054# Set the style of the frame
3055$frame31->set_shadow_type( 'etched_in' );
3056$hpaned3->add2 ($frame31);
3057$frame31->show();
3058
3059#adicionamos uma tabela dentro do frame
3060my $table31 = new Gtk2::Table (6, 1, 0);
3061#$table31->set_size_request( 144,-1 ); #width,height
3062$table31->set_border_width (2);
3063$table31->set_row_spacings(8);
3064$table31->set_col_spacings(2);
3065$frame31->add( $table31 );
3066$table31->show;
3067
3068# "child1_resize" determines whether the first child
3069# should expand when $paned is resized.
3070$hpaned3->child1_resize ($true);
3071$hpaned3->child2_resize ($true);
3072
3073# "If shrink is true, then that child can be made
3074# smaller than its requisition by the user."
3075$hpaned3->child1_shrink($false);
3076$hpaned3->child2_shrink($true);
3077
3078###-----------------final------------------###
3079###----------------hpaned3-----------------###
3080
3081###------------vpaned-playlist-------------###
3082###-----------------init-------------------###
3083# see <man Gtk2::Paned>
3084# Inside the window we put vpaned
3085# and inside vpaned we put vbox_up and vbox_down
3086
3087my $vpaned_play_list = Gtk2::VPaned->new;
3088$vpaned_play_list->set_border_width (0);
3089$vbox4->pack_start( $vpaned_play_list, $true, $true, 0 );
3090$vpaned_play_list->show;
3091
3092###------------ vbox_up playlist -----------###
3093
3094my $vbox_up_play_list = new Gtk2::VBox( $false, 2 );
3095$vbox_up_play_list->set_border_width( 1 );
3096$vpaned_play_list->add1 ($vbox_up_play_list);
3097$vbox_up_play_list->show;
3098
3099###----------- vbox_down playlist ----------###
3100
3101#my $vbox_down_play_list = new Gtk2::VBox( $false, 2 );
3102#$vbox_down_play_list->set_border_width( 1 );
3103#$vpaned_play_list->add2 ($vbox_down_play_list);
3104#$vbox_down_play_list->show;
3105
3106###-----------------------------------------###
3107
3108# "child1_resize" determines whether the first child
3109# should expand when $paned is resized.
3110$vpaned_play_list->child1_resize ($true);
3111$vpaned_play_list->child2_resize ($true);
3112
3113# "If shrink is true, then that child can be made
3114# smaller than its requisition by the user."
3115$vpaned_play_list->child1_shrink($true);
3116$vpaned_play_list->child2_shrink($true);
3117
3118###-----------------final------------------###
3119###------------vpaned-playlist-------------###
3120
3121
3122###----------------hpaned-----------------###
3123###-----------------init-------------------###
3124
3125my $hpaned_play_list = Gtk2::HPaned->new;
3126$vpaned_play_list->add2 ($hpaned_play_list);
3127$hpaned_play_list->set_border_width (0);
3128$hpaned_play_list->show;
3129
3130###------------ vbox_left playlist -----------###
3131
3132my $vbox_left_play_list = new Gtk2::VBox( $false, 2 );
3133$vbox_left_play_list->set_border_width( 1 );
3134$hpaned_play_list->add1 ($vbox_left_play_list);
3135$vbox_left_play_list->show;
3136
3137###----------- vbox_right playlist ----------###
3138
3139my $vbox_right_play_list = new Gtk2::VBox( $false, 2 );
3140$vbox_right_play_list->set_border_width( 1 );
3141$hpaned_play_list->add2 ($vbox_right_play_list);
3142$vbox_right_play_list->show;
3143
3144###-----------------------------------------###
3145
3146# "child1_resize" determines whether the first child
3147# should expand when $paned is resized.
3148$hpaned_play_list->child1_resize ($true);
3149$hpaned_play_list->child2_resize ($true);
3150
3151# "If shrink is true, then that child can be made
3152# smaller than its requisition by the user."
3153$hpaned_play_list->child1_shrink($true);
3154$hpaned_play_list->child2_shrink($true);
3155
3156#$paned->set_position ($position)
3157$hpaned_play_list->set_position ( int(44*$window_width/100) ); # 44% of $window_width
3158
3159###-----------------final------------------###
3160###----------------hpaned3-----------------###
3161
3162
3163###--------------frame4--------------------###
3164# frame4 : rip and play Audio files
3165# Adicionamos este frame4 em vbox4 (vbox4 est� dentro do notebook)
3166
3167use constant COLUMN_RIP        => 0; # check play/rip files
3168use constant COLUMN_FILE       => 1; # file  = track = row + 1
3169use constant COLUMN_ARTIST     => 2;
3170use constant COLUMN_TITLE      => 3;
3171use constant COLUMN_ALBUM      => 4;
3172use constant COLUMN_TRACK      => 5; # this is the track number
3173use constant COLUMN_LENGTH     => 6;
3174use constant COLUMN_BITRATE    => 7;
3175use constant COLUMN_YEAR       => 8;
3176use constant COLUMN_FREQUENCY  => 9;
3177use constant COLUMN_FILEPATH   => 10;
3178use constant COLUMN_EXTENSION  => 11;
3179use constant COLUMN_FILENAME   => 12;
3180use constant COLUMN_PLAY_COUNT => 13;
3181use constant COLUMN_COLOR      => 14; # to color the music playing
3182
3183my $color_row_playing = Gtk2::Gdk::Color->parse('#92D992');        # green
3184my $color_row_already_played = Gtk2::Gdk::Color->parse('#ADD8E6'); # cyan
3185
3186# see: /usr/share/doc/perl-Gtk2-1.054/gtk-demo/list_store.pl
3187my $scrolled_window = Gtk2::ScrolledWindow->new (undef, undef);
3188$scrolled_window->set_shadow_type ('etched-in');
3189$scrolled_window->set_policy('automatic', 'always');
3190$scrolled_window->set_size_request( 100, 120 ); # width, height : minimun size
3191# $vbox->pack_start( $child, $expand, $fill, $padding );
3192$vbox_up_play_list->pack_start( $scrolled_window, $true, $true, 0 );
3193$scrolled_window->show;
3194
3195# create tree view ;; $treeview_play->get_selection is an Gtk2::TreeSelection
3196my $treeview_play = Gtk2::TreeView->new( create_standard_model() ); # start with an empty array
3197$treeview_play->set ( 'rules-hint'    => $true,  # draw rows in alternating colors
3198                      'reorderable'   => $false,
3199		      'enable-search' => $true,
3200		      #'fixed-height-mode' => $true,
3201		      'search-column' => COLUMN_ARTIST,
3202		    );
3203$treeview_play->get_selection->set_mode ('multiple'); # single,multiple
3204$treeview_play->get_selection->signal_connect (changed => \&get_selection_tree, $treeview_play);
3205$treeview_play->signal_connect (button_press_event => \&button_press_event_treeview);
3206$treeview_play->signal_connect (row_activated => sub { # Double click one track to play it - 10Jun2008
3207                      my ($treeview, $path, $column) = @_;
3208
3209                      my $model = $treeview->get_model;
3210                      my $iter  = $model->get_iter ($path) if $path;
3211		      return unless $iter;
3212
3213		      if ( $files_info[$selected_row]{playing} ){   # if there are some row_playing, then change its color
3214		         push @rows_already_played, $selected_row;  # my $row_playing = $selected_row;
3215                         color_the_selected_tracks_and_scroll( select_color => 'played', scroll => $false, row => $selected_row );
3216		      }
3217		      my $row = $model->get_value($iter,COLUMN_FILE) - 1;
3218		      #print "\$row = $row ; last \$selected_row = $selected_row \$treeview = $treeview ; \$path = $path ; \$column = $column\n";
3219		      $selected_row = $row;  # This $selected_row will be played.
3220
3221		      $go_forward = $true;  # See 'sub go_forward'
3222		      play_selection( skip => 0 );
3223		      $go_forward = $false;
3224                });
3225
3226$treeview_play->set_search_equal_func ( sub {
3227                      my ($ListStore,$column,$search_name,$iter) = @_;
3228		      my $artist = $ListStore->get_value($iter,COLUMN_ARTIST);
3229		      my $row = $ListStore->get_value($iter,COLUMN_FILE) - 1;
3230		      #my $num = $ListStore->iter_n_children; # model size
3231		      if ( $artist =~ /$search_name/i ){
3232		         return $false; # to abandon the loop
3233		      }
3234		      return $true; # to don't abandon the loop
3235		});
3236$treeview_play->columns_autosize;   # lost the performance
3237$treeview_play->show;
3238
3239# add tree view to the ScrolledWindow
3240$scrolled_window->add ($treeview_play);
3241
3242sub create_model_audio_cd {
3243   my @array = @_;
3244   @files_info = ();              # reset the array
3245   %albums_already_played = ();   # reset the hash whose key is only the played album name
3246   %artists_already_played = ();  # reset the hash whose key is only the played artist name
3247   $selected_row = 0;
3248
3249   # making array of array references - see <man perllol>
3250   for (my $i=0;$i<$#array+1;$i++){
3251      my $num = sprintf("%02d", $i+1);
3252      push @files_info, {
3253	                 rip    => $true,
3254			 file   => $num,
3255	                 track  => $num,
3256			 artist => "artist",
3257			 title  => "music title",
3258			 album  => "album",
3259			 comment     => '',
3260			 year        => '',
3261			 total_track => $#array + 1,
3262			 genre       => "Other",
3263			 length      => "$array[$i]",
3264
3265			 #filepath    => "$audiodevice_path/$num".".cda", # mar2008
3266			 #directory_remain => "$audiodevice_path",
3267			 #filename    => "$num".".cda",
3268
3269			 # set all the 7 technical informations for cda:
3270			 technical_info  => "Audio CD",
3271			 bitrate         => "1411",
3272			 frequency       => "44100",
3273			 bitrate_average => $false,
3274			 mode            => $audio_cd_channel,
3275			 cda_total_time  => $audio_cd_total_time, # only for cda
3276			 fileSize        => $audio_cd_fileSize,
3277
3278			 extension_input => "cda",
3279			 editable  => $true,
3280			 show      => $true,
3281			 remove    => $false, #remove from playlist
3282			 playing   => $false,
3283			 played    => 0,
3284      };
3285   }
3286
3287   for (my $i=0;$i<$#array+1;$i++){
3288      $files_info[$i]{filepath} = make_filepath_format_for_cda($i);
3289      $files_info[$i]{filename} = make_filename_format_for_cda($i);
3290      $files_info[$i]{directory_remain} = remove_directory_base($audiodevice_path,$files_info[$i]{filepath});
3291   }
3292   #my $model = create_standard_model(@files_info);
3293   my $model = create_standard_model ( treeview => 'treeview_play', array_with_hash_ref => \@files_info );
3294
3295   return $model;
3296}
3297
3298
3299sub make_filepath_format_for_cda {
3300   my $row = shift;
3301
3302   # Filename format for rip Audio CD
3303   # %t = title of song; %a = artist; %b = album; %c = comment
3304   # %n = track number; %y = year; %g = genre
3305   # $file_format = "%a-%n-%t";
3306   my $file_format = $entry_format_cda->get_text;
3307
3308   if ($file_format !~ /(%t|%a|%b|%n|%y|%g)/ig){$file_format = "%n";}
3309   $file_format =~ s/%t/$files_info[$row]{title}/ig;
3310   $file_format =~ s/%a/$files_info[$row]{artist}/ig;
3311   $file_format =~ s/%b/$files_info[$row]{album}/ig;
3312   $file_format =~ s/%n/$files_info[$row]{track}/ig;
3313   $file_format =~ s/%y/$files_info[$row]{year}/ig;
3314   $file_format =~ s/%g/$files_info[$row]{genre}/ig;
3315
3316   my $filepath = '/' . $audiodevice_path . '/' . $file_format . '.cda';
3317   $filepath =~ s/\/{2,}/\//g;  # change two or more '//' character for one '/'
3318
3319   $filepath = remove_9_chars_and_let_1($filepath); # Do not change the character '/'.
3320
3321   return $filepath;
3322}
3323
3324sub make_filename_format_for_cda {
3325   my $row = shift;
3326
3327   # Filename format for rip Audio CD
3328   # %t = title of song; %a = artist; %b = album; %c = comment
3329   # %n = track number; %y = year; %g = genre
3330   # $file_format = "%a-%n-%t";
3331   my $file_format = $entry_format_cda->get_text;
3332
3333   if ($file_format !~ /(%t|%a|%b|%n|%y|%g)/ig){$file_format = "%n";}
3334   $file_format =~ s/%t/$files_info[$row]{title}/ig;
3335   $file_format =~ s/%a/$files_info[$row]{artist}/ig;
3336   $file_format =~ s/%b/$files_info[$row]{album}/ig;
3337   $file_format =~ s/%n/$files_info[$row]{track}/ig;
3338   $file_format =~ s/%y/$files_info[$row]{year}/ig;
3339   $file_format =~ s/%g/$files_info[$row]{genre}/ig;
3340
3341   my $filename = $file_format . '.cda';
3342   $filename =~ s/\/{2,}/\//g;      # change two or more '//' character for one '/'
3343   $filename =~ s/(.*\/)(.*)/$2/g;  # (path/)(filename): copy (path/)(filename) and get (filename)
3344
3345   $filename = remove_change_10_chars($filename);
3346
3347   return $filename;
3348}
3349
3350sub remove_change_10_chars { # 18Jun2008
3351   my $text = shift;
3352   return unless defined $text;
3353
3354   # 9 Windows Prohibited Characters      :  \/:*?"<>|
3355   # 1 perl Prohibited Character          :  $
3356   # Some Permited Characters in filenames:  =^~;!@#%�`
3357   #$text = 'Test�o��/{`:claudiofsr@yahoo.com;"}!$!?[]<|>&#*.com\/=6/1/Sfigato'; # only to test
3358
3359   $text =~ s/[\\\/:*?|\$\015]/_/g;   # replace the 7 characters \/:*?|$ with underscore _. See 'man perlretut'.
3360   $text =~ s/[`"]/'/g;                    # substitute the characters " or ` by '
3361   $text =~ tr/<>/[]/;                     # replace the characters <> with []. See 'man perlop'.
3362
3363   #$text =~ s/[^[:print:]]/_/g;           # replace unprintable characters with underscores.
3364   #print "remove_change_10_chars --> \$text = $text\n";
3365   return $text;
3366}
3367
3368sub remove_9_chars_and_let_1 { # 18Jun2008 - Do not change the character '/'.
3369   my $text = shift;
3370   return unless defined $text;
3371
3372   # 9 Windows Prohibited Characters in filenames:  \/:*?"<>|
3373   # 1 perl Prohibited Character                 :  $
3374   # Remove/Change 9 Characters  \:*?"<>|$ in filenames and let 1 Characters /
3375
3376   $text =~ s/[\\:*?|\$]/_/g;   # replace the 6 characters \:*?|$ with underscore _. See 'man perlretut'.
3377   $text =~ s/[`"]/'/g;         # substitute the characters " or ` by '
3378   $text =~ tr/<>/[]/;          # replace the characters <> with []. See 'man perlop'.
3379
3380   return $text;
3381}
3382
3383sub remove_directory_base {
3384    my ($dir_base,$file) = @_;
3385    ( my $dir = $file ) =~ s/(.*\/)(.*)/$1/g;  # supose that $file = (/path1/path2/path3/path4/)(filename). So remove (filename) from full path.
3386    $dir =~ s/^\Q$dir_base\E//;                # supose that $directory_base = (/path1/path2), then directory_ramin = (/path3/path4/)
3387
3388    # Caution with metacharacters: {}[]()^$.|*+?\
3389    # To match this metacharacters use the escape sequence "\Q"..."\E" (see man perlretut)
3390    return $dir;
3391}
3392
3393sub create_model_files_to_be_played {  # add more file to @files_info(); player mode
3394
3395   if ( not defined $files_info[0]{file} ){ @files_info = (); }
3396   #print "--->  n = ",$#files_info + 1,"\n";
3397   my $music_number = $#files_info + 1;  # inicial size
3398
3399   my $j = 0;
3400   for (my $i = 0; $i <= $#array_of_files; $i++){  # ($#array_of_files + 1) or @array is the size
3401
3402      my %hash = determine_directory_and_filename_and_extension(filepath => $array_of_files[$i]);
3403      my $extension_input = $hash{extension_input};
3404
3405      my $mesg = filename_from_unicode "\n<$array_of_files[$i]> : Not found a player to $extension_input files!\n";
3406      my $playable = $true;
3407
3408      if    ($extension_input eq 'mp3'  and not ($mpg123_path or $mpg321_path or $madplay_path or $mplayer_path) ){ $playable = $false; }
3409      elsif ($extension_input eq 'mp4'  and not $mplayer_path                                                    ){ $playable = $false; }
3410      elsif ($extension_input eq 'mpc'  and not ($mpcdec_path or $mplayer_path)                                  ){ $playable = $false; }
3411      elsif ($extension_input eq 'ogg'  and not ($ogg123_path or $mplayer_path)                                  ){ $playable = $false; }
3412      elsif ($extension_input eq 'ape'  and not ( ($ape_path and ($play_path or $aplay_path)) or $mplayer_path ) ){ $playable = $false; }
3413      elsif ($extension_input eq 'flac' and not ($flac123_path or $mplayer_path)                                 ){ $playable = $false; }
3414      elsif ($extension_input eq 'wav'  and not ($aplay_path or $mplayer_path)                                   ){ $playable = $false; }
3415
3416      if ($playable) { $j += 1; }
3417      else { print $mesg; next; }
3418
3419      # making array with anonymous hashes - see <man perlref>
3420      push @files_info, {
3421                          rip       => $true,
3422	                  file      => $j + $music_number,
3423
3424	                  filepath         => $array_of_files[$i],
3425			  #filepath        => eval_to_unicode($array_of_files[$i]),
3426                          #filepath_local   => $array_of_files[$i],
3427
3428			  directory_remain => remove_directory_base($directory_base,$array_of_files[$i]),
3429			  filename         => $hash{filename},
3430			  directory        => $hash{directory},
3431			  extension_input  => $extension_input,
3432
3433			  editable  => $true,
3434			  show      => $true,
3435			  remove    => $false, #remove from playlist
3436			  playing   => $false,
3437			  played    => 0,
3438                        };
3439   }
3440
3441   for (my $row = $music_number; $row <= $#files_info ; $row++){
3442
3443      # use the new Gtk2::FileChooser
3444      my $total_files = $#files_info + 1 - $music_number;
3445      my $cont = $row + 1 - $music_number;
3446      my $percentage  = number_value( sprintf("%.2f", ( $cont/$total_files ) ) );
3447      my $percentage2 = number_value( sprintf("%.2f", ( $cont/$total_files * 100) ) );
3448      my $filename = $files_info[$row]{filename};
3449
3450      $label_pct->set_label("$cont/$total_files (${percentage2}%)");
3451
3452      $progbar_files_info->set_text( reduce_length_size($filename, 100) );  # Gtk2::ProgressBar to show file info
3453      $progbar_files_info->set_fraction ($percentage);                      # $fraction (double)
3454
3455      #printf "Percentage = $percentage ; filename = $filename\n";
3456      #print "\ncreate_model_files_to_be_played --> \$music_number = $music_number;new files = $total_files;\$cont = $cont\n";
3457
3458      get_info_from_file( row => $row,
3459                          filepath => $files_info[$row]{filepath},
3460                          extension_input => $files_info[$row]{extension_input},
3461			  show_tag => $false,
3462                        );
3463
3464      while (Gtk2->events_pending) {Gtk2->main_iteration};
3465
3466   # get the 8 tags
3467   #$files_info[$row]{title}  = $Title;
3468   #$files_info[$row]{artist}  = $Artist;
3469   #$files_info[$row]{album}  = $Album;
3470   #$files_info[$row]{track}  = $Track_Number;
3471   #$files_info[$row]{total_track}  = $Total_Track;
3472   #$files_info[$row]{comment}  = $Comment;
3473   #$files_info[$row]{year}  = $Year;
3474   #$files_info[$row]{genre}  = $Genre;
3475   # get all technical informations:
3476   #$files_info[$row]{technical_info}  = $Technical_Info;
3477   #$files_info[$row]{bitrate}  = $bitrate_original;
3478   #$files_info[$row]{bitrate_average}  = $bitrate_average;
3479   #$files_info[$row]{frequency}  = $Frequency;
3480   #$files_info[$row]{mode}  = $mode_channel;
3481   #$files_info[$row]{length}  = $Time_Min_Sec;
3482   #$files_info[$row]{fileSize}  = $fileSize;
3483   }
3484
3485   # get all artists/albums one times only
3486   #for (my $i = 0; $i <= $#files_info; $i++){
3487
3488      # $hash{'artist_names'} = [artist_1,artist_2, ..., artist_n]; # anonymous array
3489      # To append a new value: push @{ $hash{'artist_names'} }, $row;
3490      #my @artists = return_artist_name($files_info[$i]); # $files_info[$i] is a hash
3491      #foreach my $art (@artists) { push @{ $files_info[$i]{artist_names} }, $art; }
3492
3493      #$files_info[$i]{album_names} = return_album_name($files_info[$i]);
3494   #}
3495
3496   #my $model = create_standard_model(@files_info);
3497   my $model = create_standard_model ( treeview => 'treeview_play', array_with_hash_ref => \@files_info );
3498
3499   return $model;
3500}
3501
3502my %treeview_play_rows;   # used to find $iter and $path without need to search inside all treeview using loop.
3503my %treeview_artist_rows;
3504my %treeview_album_rows;
3505#create_standard_model ( treeview => 'treeview_play', array_with_hash_ref => \@files_info );
3506
3507sub create_standard_model {  # 12Jun2008
3508   my %args = (  treeview            => 'treeview_play', # default value
3509                 array_with_hash_ref => undef,
3510                 @_,                 # argument pair list goes here
3511   );
3512   my $model = Gtk2::ListStore->new ('Glib::Boolean', # => G_TYPE_BOOLEAN
3513                                     'Glib::Uint',    # => G_TYPE_UINT
3514				     'Glib::String',
3515				     'Glib::String',  # => G_TYPE_STRING
3516                                     'Glib::String',  # => G_TYPE_STRING
3517                                     'Glib::String',  # you get the idea
3518				     'Glib::String', 'Glib::Uint',   # length, bitrate
3519				     'Glib::String', 'Glib::Uint',   # year, freq , Glib::Double
3520				     'Glib::String', 'Glib::String', # filepath, extension_input
3521				     'Glib::String',                 # filename
3522				     'Glib::Uint',                   # count play times
3523				     'Gtk2::Gdk::Color',             # to select one color
3524				    );
3525
3526   my $rrow = 0; # relative row that is showed on treeview
3527
3528   if    ( $args{treeview} eq 'treeview_play'   ) { %treeview_play_rows   = ();  }
3529   elsif ( $args{treeview} eq 'treeview_artist' ) { %treeview_artist_rows = () ; }
3530   elsif ( $args{treeview} eq 'treeview_album'  ) { %treeview_album_rows  = () ; }
3531
3532   # append more data to the list store
3533   foreach my $hash ( @{$args{array_with_hash_ref}} ) {  # print "\$hash->{file} = $hash->{file} ; \$hash->{rip} = $hash->{rip}\n";
3534      next if ($hash->{remove});
3535
3536      my $iter = $model->append;
3537      $model->set ($iter,
3538		     COLUMN_RIP,       $hash->{rip},  # rip or play
3539		     COLUMN_FILE,      $hash->{file}, # file number. The total row number is: row = $hash->{file} - 1
3540                     COLUMN_TRACK,     $hash->{track},
3541		     COLUMN_ARTIST,    $hash->{artist},
3542	             COLUMN_TITLE,     $hash->{title},
3543		     COLUMN_ALBUM,     $hash->{album},
3544	             COLUMN_LENGTH,    $hash->{length},
3545		     COLUMN_BITRATE,   $hash->{bitrate},
3546		     COLUMN_YEAR,      $hash->{year},
3547		     COLUMN_FREQUENCY, $hash->{frequency},
3548                     COLUMN_FILEPATH,  $hash->{filepath},
3549		    #COLUMN_FILEPATH,  $hash->{directory_remain},
3550		     COLUMN_EXTENSION, $hash->{extension_input},
3551		     COLUMN_FILENAME,  $hash->{filename},
3552		     COLUMN_PLAY_COUNT,$hash->{played}, # played n times
3553		     COLUMN_COLOR,     $hash->{playing} ? $color_row_playing :
3554		                       $hash->{played}  ? $color_row_already_played : undef, # undef: without color; this column is hidden
3555                  );
3556
3557      if    ( $args{treeview} eq 'treeview_play'   ) { $treeview_play_rows  { $hash->{file} - 1     } = $rrow; } # row = track - 1
3558      elsif ( $args{treeview} eq 'treeview_artist' ) { $treeview_artist_rows{ uc( $hash->{artist} ) } = $rrow; }
3559      elsif ( $args{treeview} eq 'treeview_album'  ) { $treeview_album_rows { uc( $hash->{album} )  } = $rrow; }
3560
3561      #if    ( $args{treeview} eq 'treeview_artist' ) { print "\$treeview_artist_rows{ $hash->{artist} } = $treeview_artist_rows{ uc( $hash->{artist} ) } \n"; }
3562      #elsif ( $args{treeview} eq 'treeview_album'  ) { print "\$treeview_album_rows{ $hash->{album} } = $treeview_album_rows{ uc( $hash->{album} ) } \n";     }
3563
3564      $rrow += 1;
3565      #print "\$hash->{artist} = $hash->{artist} ; \$hash->{played} = $hash->{played}\n";
3566   }
3567   return $model;
3568}
3569
3570
3571my $treeview_artist;
3572my $renderer_artist_playlist; my $column_artist_playlist;  my $renderer_artist_play_count;
3573my $renderer_album_playlist;  my $column_album_playlist;   my $renderer_album_play_count;
3574
3575###-------------- scrolled_window_album ------------###
3576###---------------------- start --------------------###
3577
3578my $scrolled_window_album = Gtk2::ScrolledWindow->new (undef, undef);
3579$scrolled_window_album->set_shadow_type ('etched-in');
3580$scrolled_window_album->set_policy('automatic', 'always');
3581$scrolled_window_album->set_size_request( 80, 60 ); # width, height : minimun size
3582# $vbox->pack_start( $child, $expand, $fill, $padding );
3583$vbox_right_play_list->pack_start( $scrolled_window_album, $true, $true, 0 );
3584$scrolled_window_album->show;
3585
3586# create tree view
3587my $treeview_album = Gtk2::TreeView->new( create_standard_model() ); # start with an empty array
3588$treeview_album->set ( 'rules-hint'  => $true,  # draw rows in alternating colors
3589                       'reorderable' => $false,
3590		     );
3591#$treeview_album->set_search_column (COLUMN_ALBUM);
3592$treeview_album->get_selection->set_mode ('multiple'); # single,multiple
3593$treeview_album->get_selection->signal_connect (changed => \&selection_album);
3594$treeview_album->show;
3595# add tree view to the ScrolledWindow
3596$scrolled_window_album->add ($treeview_album);
3597
3598# album column
3599   $renderer_album_playlist = Gtk2::CellRendererText->new;
3600   $renderer_album_playlist->set_data (column => COLUMN_ALBUM);
3601   $renderer_album_playlist->set ('font' => $window_font_name, style => 'italic', scale => '0.9');
3602
3603   $column_album_playlist = Gtk2::TreeViewColumn->new_with_attributes ($langs{name053}, $renderer_album_playlist, text => COLUMN_ALBUM,
3604                                                                       'cell-background-gdk' => COLUMN_COLOR);
3605   $column_album_playlist->set_resizable ($true);
3606   $column_album_playlist->set (alignment => 0.0); # Alignment of the column header text
3607   $column_album_playlist->set_expand ($true);     # this column expand to max width
3608   $column_album_playlist->set_sort_column_id (COLUMN_ALBUM);
3609   $treeview_album->insert_column ($column_album_playlist, COLUMN_ALBUM);
3610
3611#add_columns_album_artist( column_show_play_count => $column_show_play_count);
3612
3613#-----------------------------------------------------#
3614
3615#my %albums_already_played;             # hash whose key is only the played album name
3616#$albums_already_played{uc($album)}++;  # Set True to the album already played
3617#if ( $albums_already_played{uc($album)} ){ }
3618
3619#-----------------------------------------------------#
3620# See "The Schwartzian Transform" -- "Learning Perl Objects, References, and Modules", Chap.7.4, First Edition
3621# See "Perl CookBook, Chap 4.16 and 'A Fresh Look at Efficient Perl Sorting' by Uri Guttman and Larry Rosler.
3622# Perl's simple sort function is O(N) efficiency order.
3623# Perl's advanced sort function, like sort { uc($a) cmp uc($b) },  is O(N*logN) efficiency order.
3624# The 'packed-default sort' technique is quasi-O(N) behavior.
3625
3626# To obtain an array without repeated values: my %unique = map {$_ => 1}, @numbers;
3627# search for all differents albums "efficiently". Efici�ncia: uso racional do recursos, cpu, processamento.
3628
3629sub all_albums2 {  # Using the 'Schwartzian Transform sort' technique. - Not used
3630   my @array_of_hash = @_;  # @files_info
3631   if ( not defined $array_of_hash[0]{album} ){ return; }
3632
3633   my %seen = ();  # hash ; save all differents albums indicating the total songs inside each album
3634
3635   my @temp = map  { album => $_->[0] , played => $albums_already_played{uc($_->[0])} }, # set 'played' to False if $albums_already_played{uc($_)} is undef
3636              sort { $a->[1] cmp $b->[1] }     # sort the uc(album name); all elements are already differents
3637              map  [ $_, uc($_) ],             # Sort the references to anonymous arrays that contain the original data $_ and the uc($_).
3638	      #keys %{{ map {return_album_name($_) => 1} @array_of_hash }};
3639              grep { not $seen{uc($_)} ++ }    # Eliminate duplicate values from a list; Perl CookBook , Chap 4.7
3640	      map  return_album_name($_),      # $_ is an element of @array_of_hash
3641	      @array_of_hash;
3642
3643   #foreach my $k (sort keys %seen){ print "$k => $seen{$k} songs\n"; }
3644
3645   my @all_albums = ( { album => 'All' , played => 0 } );  # 'All' is Always the first element
3646   push @all_albums, @temp;
3647
3648   #foreach my $alb (@all_albums) { print "\$alb->{album} = $alb->{album}\n";  }
3649   return @all_albums;
3650}
3651
3652sub all_albums3 {  # Using the 'packed-default sort' technique. - Not used
3653   my @array_of_hash = @_;  # @files_info
3654   if ( not defined $array_of_hash[0]{album} ){ return; }
3655
3656   my %seen = ();  # hash ; save all differents albums indicating the total songs inside each album
3657
3658   my @temp = map  { album => $_->[0] , played => $albums_already_played{uc($_->[0])} }, # set 'played' to False if $albums_already_played{uc($_)} is undef
3659	      sort { $a->[1] cmp $b->[1] }
3660              map  [ $_, pack( 'C4' => /(\d+)\.(\d+)\.(\d+)\.(\d+)/ ) . uc($_) ],
3661              keys %{{ map {return_album_name($_) => 1} @array_of_hash }};
3662
3663  # The 'packed-default sort' is a little-known optimization that improves on the Schwartzian Transform (ST) by eliminating the sortsub itself,
3664  # relying on the default lexicographic sort, which is very efficient. This is the method used in the new Sort::Maker module.
3665  # The packed-default sort is about twice as fast as the ST, which is the fastest familiar Perl sorting algorithm.
3666
3667   my @all_albums = ( { album => 'All' , played => 0 } );  # 'All' is Always the first element
3668   push @all_albums, @temp;
3669
3670   #foreach my $alb (@all_albums) { print "\$alb->{album} = $alb->{album}\n";  }
3671   return @all_albums;
3672}
3673
3674sub all_albums {  # Using 'My Best' technique.
3675   my @array_of_hash = @_;  # @files_info
3676   if ( not defined $array_of_hash[0]{album} ){ return; }
3677
3678   #my %unique  = map { uc(return_album_name($_)) => return_album_name($_) } @array_of_hash;  # use the 'sub return_album_name' 2*N times
3679   my %unique1 = map { return_album_name($_) => 1  } @array_of_hash; # Eliminate duplicate values from a list.
3680   my %unique2 = map { NFKD(uc($_))          => $_ } keys %unique1;  # Sort the keys and get the values from hash.
3681
3682   my @all_albums = ( { album => 'All' , played => 0 } );   # 'All' is Always the first element
3683   foreach (sort keys %unique2){                            # sort compared lexicographically improves the performance of all sorting techniques
3684      push @all_albums, { album => $unique2{$_} , played => $albums_already_played{uc($unique2{$_})} };
3685      #print "$_ => $unique{$_} \n";       # set 'played' = 0 if album not played.
3686   }
3687   return @all_albums;
3688}
3689
3690sub return_album_name {
3691   my $hash = shift;
3692   my $album = $hash->{album};
3693   if ($album eq ""){                                                      # if the album name is empty
3694      ($album = $hash->{filepath}) =~ s/.*\/(.*)\/.*$/\(directory\)$1/go;  # get the last directory name
3695   }
3696   return $album;
3697}
3698
3699# get treeview_album selection and return the tracks inside the selected album
3700sub selection_album {
3701   my $model = $treeview_album->get_model;
3702   my $tree_selection = $treeview_album->get_selection;
3703   my @paths = $tree_selection->get_selected_rows; # sel = Gtk2::TreePath
3704   return unless @paths;
3705
3706   my %albums_selected;  # hash whose key is the album name
3707   foreach my $path (@paths){
3708      my $iter = $model->get_iter($path);
3709      next unless $iter;
3710      my $album = $model->get_value($iter,COLUMN_ALBUM);
3711      $albums_selected{uc($album)}++;  # Set True to the album name;  See 'Programming Perl', 24.2, 3a. Ed, "Efficiency"
3712   }
3713   #foreach my $alb (keys %albums_selected) { print "albums = $alb\n"; }
3714   my @tracks_in_the_album;
3715
3716   LOOP:for (my $i = 0; $i <= $#files_info; $i++){
3717      next if $files_info[$i]->{remove};                       # create_standard_model will filter this.
3718      my $album_guess = return_album_name($files_info[$i]);    # if no album name, create a guess name from filepath
3719
3720      if ( $albums_selected{uc($album_guess)} or $albums_selected{uc('All')} ){  # Search for all tracks inside this album
3721         push @tracks_in_the_album, $files_info[$i];
3722	 next LOOP;
3723      }
3724   }
3725   return if (@tracks_in_the_album <= 0);
3726
3727   # add new files to tree model
3728   #$model = create_standard_model(@tracks_in_the_album);
3729   $model = create_standard_model ( treeview => 'treeview_play', array_with_hash_ref => \@tracks_in_the_album );
3730
3731   $treeview_play->set_model($model);   # add the new model
3732   #$treeview_play->columns_autosize;   # lost the performance
3733
3734   count_artists_and_album_already_played(update_model_artist => $false, update_model_album => $true);
3735
3736   #foreach (@tracks_in_the_album){print " --> array = \"$_->{artist}\"\n";}
3737   make_artist_and_album_treeview_column( make_artist_column => $true, make_album_column => $false, array_with_tracks => \@tracks_in_the_album );
3738
3739   return unless $playing_music;
3740   color_the_selected_tracks_and_scroll( select_color => 'playing', color_album => $false );
3741}
3742
3743sub make_artist_and_album_treeview_column {   # make artist and album treeview column with an array
3744   my %args = ( make_artist_column => $true,
3745                make_album_column  => $true,
3746		array_with_tracks  => undef,  # reference to array with hash
3747                @_,     # argument pair list goes here
3748	      );
3749   my @selected_info = @{$args{array_with_tracks}};         # get array from reference_to_array
3750   return unless defined $selected_info[0]{file};
3751   my ( $new_title, $model );
3752
3753   if ( $args{make_artist_column} ){
3754      my @all_dif_artist = all_artists( @selected_info );    # search for all differents artists
3755      $model = create_standard_model ( treeview => 'treeview_artist', array_with_hash_ref => \@all_dif_artist );
3756
3757      $treeview_artist->set_model($model);                  # add the new model
3758      $new_title = @all_dif_artist > 0 ? $langs{name046} ." ($#all_dif_artist)" : $langs{name046} ;
3759      $column_artist_playlist->set_title( $new_title );     # artist (number)
3760   }
3761   if ( $args{make_album_column} ){
3762      my @all_dif_album = all_albums( @selected_info );     # search for all differents albums
3763      $model = create_standard_model ( treeview => 'treeview_album', array_with_hash_ref => \@all_dif_album );
3764
3765      $treeview_album->set_model($model);                   # add the new model
3766      $new_title = @all_dif_album > 0 ? $langs{name053} ." ($#all_dif_album)" : $langs{name053} ;
3767      $column_album_playlist->set_title( $new_title );      # album (number)
3768   }
3769}
3770
3771sub count_artists_and_album_already_played { # 09Jun2008
3772   my %args = ( update_model_artist  => $false,
3773		update_model_album   => $false,
3774                @_,  # argument pair list goes here
3775	      );
3776   my $model;
3777   my @rows_checked   = ();
3778   my @rows_unchecked = ();
3779
3780   for (my $row = 0; $row <= $#files_info; $row++){
3781      if ($files_info[$row]{rip} eq $true){ push @rows_checked,   $row; }  # Get array_with_files_checked
3782      else                                { push @rows_unchecked, $row; }  # To fill with the unchecked rows;
3783   }
3784
3785   %artists_already_played = ();  # played artists count
3786   %albums_already_played  = ();  # played albums count
3787   my $debug = $false;            # to print messages
3788
3789   print "\n\n --> count_artists_and_album_already_played:\n" if $debug;
3790
3791   # refresh Play Count artist
3792
3793   foreach my $row (@rows_checked){
3794      my @artists = return_artist_name($files_info[$row]);
3795      foreach my $art (@artists) { $artists_already_played{ uc($art) }  = 0; }  # Set = 0 if artist not played
3796   }
3797   foreach my $row (@rows_unchecked){
3798      my @artists = return_artist_name($files_info[$row]);
3799      foreach my $art (@artists) { $artists_already_played{ uc($art) } += 1; }  # Add +1
3800   }
3801
3802   #($iter, $path) = get_iter_path_given_track_art_alb ( artist => $artist, treeview => 'treeview_artist' );
3803   #$model->set ( $iter, COLUMN_COLOR, $color ) if $iter;  # to color the selected artist line
3804
3805   if ( $args{update_model_artist} ){
3806      $model = $treeview_artist->get_model;
3807      $model->foreach( sub{  # model is a $ListStore
3808         my($ListStore,$path,$iter) = @_;
3809         my $artist = $model->get ( $iter, COLUMN_ARTIST) if $iter;
3810         $model->set ( $iter, COLUMN_PLAY_COUNT, $artists_already_played{ uc($artist) } ) if $iter;  # to color the selected artist line
3811         print "artists = $artist ; \$artists_play_count{$artist} = $artists_already_played{uc($artist)}\n" if $debug;
3812         return $false; # to don't abandon the loop
3813      } );
3814   }
3815
3816   # refresh Play Count album
3817
3818   foreach my $row (@rows_checked){
3819      my $album = return_album_name($files_info[$row]); # if no album name, create a guess name from filepath
3820      $albums_already_played{ uc($album) }  = 0;        # Set = 0 if album not played
3821   }
3822   foreach my $row (@rows_unchecked){
3823      my $album = return_album_name($files_info[$row]); # if no album name, create a guess name from filepath
3824      $albums_already_played{ uc($album) } += 1;        # Add +1
3825   }
3826
3827   if ( $args{update_model_album} ){
3828      $model = $treeview_album->get_model;
3829      $model->foreach( sub{  # model is a $ListStore
3830         my($ListStore,$path,$iter) = @_;
3831         my $album = $model->get ( $iter, COLUMN_ALBUM) if $iter;
3832         $model->set ( $iter, COLUMN_PLAY_COUNT, $albums_already_played{ uc($album) } ) if $iter;   # to color the selected artist line
3833         print "album = $album ; \$albums_already_played{$album} = $albums_already_played{uc($album)}\n" if $debug;
3834         return $false; # to don't abandon the loop
3835      } );
3836   }
3837}
3838
3839###---------------------- final --------------------###
3840###-------------- scrolled_window_album ------------###
3841
3842###-------------- scrolled_window_artist ------------###
3843###---------------------- start --------------------###
3844
3845my $scrolled_window_artist = Gtk2::ScrolledWindow->new (undef, undef);
3846$scrolled_window_artist->set_shadow_type ('etched-in');
3847$scrolled_window_artist->set_policy ('automatic', 'always');
3848$scrolled_window_artist->set_size_request( 80, 20 ); # width, height : minimun size
3849# $vbox->pack_start( $child, $expand, $fill, $padding );
3850$vbox_left_play_list->pack_start( $scrolled_window_artist, $true, $true, 0 );
3851$scrolled_window_artist->show;
3852
3853# create tree view
3854$treeview_artist = Gtk2::TreeView->new( create_standard_model() ); # start with an empty array
3855$treeview_artist->set ( 'rules-hint'  => $true,  # draw rows in alternating colors
3856                        'reorderable' => $false,
3857		      );
3858#$treeview_artist->set_search_column (COLUMN_ARTIST);
3859$treeview_artist->get_selection->set_mode ('multiple'); # single,multiple
3860$treeview_artist->get_selection->signal_connect (changed => \&selection_artist);
3861$treeview_artist->show;
3862# add tree view to the ScrolledWindow
3863$scrolled_window_artist->add ($treeview_artist);
3864
3865   # artist column
3866   $renderer_artist_playlist = Gtk2::CellRendererText->new;
3867   $renderer_artist_playlist->set_data (column => COLUMN_ARTIST);
3868   $renderer_artist_playlist->set ('font' => $window_font_name, style => 'italic', scale => '0.9');
3869
3870   $column_artist_playlist = Gtk2::TreeViewColumn->new_with_attributes ($langs{name046}, $renderer_artist_playlist, text => COLUMN_ARTIST,
3871                                                                        'cell-background-gdk' => COLUMN_COLOR);
3872   $column_artist_playlist->set_resizable ($true);
3873   $column_artist_playlist->set (alignment => 0.0); # Alignment of the column header text
3874   $column_artist_playlist->set_expand ($true);     # this column expand to max width
3875   $column_artist_playlist->set_sort_column_id (COLUMN_ARTIST);
3876   $treeview_artist->insert_column ($column_artist_playlist, COLUMN_ARTIST);
3877
3878add_columns_album_artist( column_show_play_count => $column_show_play_count);
3879
3880#-----------------------------------------------------#
3881
3882#my %artists_already_played;              # hash whose key is only the played artist name
3883#$artists_already_played{uc($artist)}++;  # Set True to the artist already played
3884
3885#-----------------------------------------------------#
3886
3887sub all_artists {
3888   my @array_of_hash = @_;
3889   if ( not defined $array_of_hash[0]{artist} ){ return; }
3890
3891   my @all_art = map return_artist_name($_), @array_of_hash;
3892   my %unique  = map { NFKD(uc($_)) => $_ }  @all_art;        # Eliminate duplicate values from a list;
3893
3894   my @all_artists = ( { artist => 'All' , played => 0 } );   # 'All' is Always the first element
3895   foreach (sort keys %unique){                               # sort compared lexicographically improves the performance of all sorting techniques
3896      push @all_artists, { artist => $unique{$_} , played => $artists_already_played{ uc($unique{$_}) } };
3897      #print "$_ => $unique{$_} \n";
3898   }
3899   return @all_artists;
3900}
3901
3902
3903sub return_artist_name {
3904   my $hash = shift;
3905   return undef if not $hash->{artist};          # 'if not' == 'unless'
3906   return split (/\s*&\s*/, $hash->{artist});    # split 'artist1 & artist2 & ...' to 'artist1,artist2,...'
3907}
3908
3909# get treeview_artist selection and return the track selected
3910sub selection_artist {
3911   my $model = $treeview_artist->get_model;
3912   my $tree_selection = $treeview_artist->get_selection;
3913   my @paths = $tree_selection->get_selected_rows; # sel = Gtk2::TreePath
3914   return unless @paths;
3915
3916   my %artists_selected;
3917   foreach my $path (@paths){
3918      my $iter = $model->get_iter($path);
3919      next unless $iter;
3920      my $artist = $model->get_value($iter,COLUMN_ARTIST);
3921      $artists_selected{uc($artist)}++;  # See 'Programming Perl', 24.2, 3a. Ed, "Efficiency"
3922   }
3923   my @tracks_with_artist;
3924
3925   LOOP:for (my $i = 0; $i <= $#files_info; $i++){
3926      next if $files_info[$i]->{remove};
3927      my @artist_cmp = return_artist_name($files_info[$i]); # 'art1 & art2' return (art1,art2)
3928
3929      foreach my $art (@artist_cmp){
3930	 if ( $artists_selected{uc($art)} or $artists_selected{uc('All')} ){
3931	    push @tracks_with_artist, $files_info[$i];
3932	    next LOOP; # to not select the same files_info, not duplicate
3933         }
3934      }
3935   }
3936   return if (@tracks_with_artist <= 0);
3937
3938   # add new files to tree model
3939   #$model = create_standard_model(@tracks_with_artist);
3940   $model = create_standard_model ( treeview => 'treeview_play', array_with_hash_ref => \@tracks_with_artist );
3941
3942   $treeview_play->set_model($model); # add the new model
3943   #$treeview_play->columns_autosize;
3944
3945   count_artists_and_album_already_played(update_model_artist => $true, update_model_album => $false);
3946
3947   #my $row = $tracks_with_artist[0]{file} - 1; # [0] is the fisrt track
3948   make_artist_and_album_treeview_column( make_artist_column => $false, make_album_column => $true, array_with_tracks => \@tracks_with_artist );
3949
3950   return unless $playing_music;
3951   color_the_selected_tracks_and_scroll( select_color => 'playing', color_artist => $false );
3952}
3953
3954#-----------------------------------------------------#
3955my @paths_button; # to save the selected rows
3956
3957sub button_press_event_treeview { # see /usr/share/doc/perl-Gtk2-1.083/examples/multisel.pl
3958   my ($widget, $event, $data) = @_;    # $widget = Gtk2::TreeView
3959   @paths_button = ();
3960   return unless ($event->button >= 3); # Press mouse button 3 ; see <man Glib>
3961
3962   my $selection = $widget->get_selection;
3963   @paths_button = $selection->get_selected_rows; # sel = Gtk2::TreePath
3964   #my @sel_rows = map $_->to_string, @paths_button;
3965   #print "\n button3 ---> \@sel_rows = @sel_rows";
3966   return unless @paths_button;
3967
3968   # [[$treeview_play->get_selection]] is an Gtk2::TreeSelection
3969   #$treeview_play->get_selection->unselect_all;
3970
3971   my $menu = Gtk2::Menu->new;
3972   my $item = Gtk2::ImageMenuItem->new("$langs{msg092}");   # "remove this music from list"
3973   $item->set_image( Gtk2::Image->new_from_stock ('gtk-delete','menu') );
3974   $item->signal_connect( "activate", \&remove_item_from_playlist, $treeview_play );
3975   $menu->append($item);
3976   # $menu->popup ($parent_menu_shell, $parent_menu_item, $menu_pos_func, $data, $button, $activate_time)
3977   $menu->popup(undef, undef, undef,undef,$event->button,$event->time);
3978   $menu->show_all;
3979}
3980
3981# $button->signal_connect (clicked => \&remove_item, $treeview);
3982sub remove_item_from_playlist {  # See /usr/share/doc/perl-Gtk2-1.083/gtk-demo/editable_cells.pl
3983  my ($widget, $treeview) = @_;
3984  my $model = $treeview->get_model;
3985  $treeview->set_reorderable ($true);
3986
3987  #while (@paths_button > 0) {
3988  #   my $path = splice(@paths_button, -1);  # remove the last element of @array
3989
3990  foreach my $path (reverse @paths_button){  # descending order
3991     my $iter = $model->get_iter($path);
3992     if ($iter) {
3993        my $row_abs = $model->get_value($iter,COLUMN_FILE) - 1; # absolute position into @files_info
3994        #print "row_abs = $row_abs ; remove filepath = $files_info[$row_abs]->{filepath} ; files_info_number = ",$#files_info+1,"\n";
3995
3996        #splice (@files_info, $row_abs, 1);  # remove one element
3997        $files_info[$row_abs]->{remove}=$true;
3998	$model->remove($iter);
3999     }
4000  }
4001}
4002
4003###---------------------- final --------------------###
4004###------------- scrolled_window_artist ------------###
4005
4006###--------------------- frame5 --------------------###
4007###--------------------- in�cio --------------------###
4008# we put frame5a + frame5b inside vbox5 and then inside notebook5
4009my $frame5a = new Gtk2::Frame( $langs{name058} );
4010$frame5a->set_label_align( 1.0, 0.0 );
4011$frame5a->set_shadow_type( 'etched_out' );
4012$vbox5->pack_start( $frame5a, $true, $true, 0 );
4013$frame5a->show();
4014
4015#adicionamos uma tabela dentro do frame
4016#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
4017my $table5a = new Gtk2::Table (3, 4, 0);
4018$table5a->set_border_width (4);
4019$table5a->set_row_spacings(4);
4020$table5a->set_col_spacings(10);
4021$frame5a->add( $table5a );
4022$table5a->show;
4023
4024my $frame5b = new Gtk2::Frame( $langs{name059} );
4025$frame5b->set_label_align( 1.0, 0.0 );
4026$frame5b->set_shadow_type( 'etched_out' );
4027$vbox5->pack_start( $frame5b, $true, $true, 0 );
4028$frame5b->show();
4029
4030#adicionamos uma tabela dentro do frame
4031#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
4032my $table5b = new Gtk2::Table (3, 4, 0);
4033$table5b->set_border_width (4);
4034$table5b->set_row_spacings(4);
4035$table5b->set_col_spacings(10);
4036$frame5b->add( $table5b );
4037$table5b->show;
4038
4039#-----------------------table5a-----------------------#
4040$ComboBox_Names = Gtk2::ComboBox->new_text;
4041foreach (sort keys %langs) { $ComboBox_Names->append_text ($_); }
4042$ComboBox_Names->set_active($Names_Active_Num); #init value
4043$ComboBox_Names->set_size_request( 92, -1 );
4044$ComboBox_Names->signal_connect("changed", \&show_textview_language, $ComboBox_Names);
4045$tooltips->set_tip($ComboBox_Names, $langs{tip501}) if $show_all_tooltips;
4046$table5a->attach ($ComboBox_Names, 0, 1, 0, 1, ['expand','fill'], 'shrink', 0, 0);
4047$ComboBox_Names->set_add_tearoffs($true);  # Whether dropdowns should have a tearoff menu item
4048$ComboBox_Names->show;
4049
4050$label = new Gtk2::Label( $langs{name016} . ":" );
4051$label->set_alignment( 1.0, 0.5 );
4052$table5a->attach ($label, 1, 2, 0, 1, 'fill', 'shrink', 0, 0);
4053$label->show;
4054
4055$ComboBox_Lang_5a = Gtk2::ComboBox->new_text;
4056foreach (sort keys %all_language) { $ComboBox_Lang_5a->append_text ($_); }
4057#foreach (sort { uc($a) cmp uc($b) } keys %all_language) { $ComboBox_Lang_5a->append_text ($_); }
4058
4059my $pos = 0;
4060foreach my $key (sort keys %all_language) { if ($key eq $language) {$ComboBox_Lang_5a->set_active($pos); last;} $pos++; }
4061
4062$ComboBox_Lang_5a->set_size_request( 92, -1 );
4063$ComboBox_Lang_5a->signal_connect("changed", \&lang_choice5a);
4064$tooltips->set_tip($ComboBox_Lang_5a, $langs{tip502});
4065$table5a->attach ($ComboBox_Lang_5a, 3, 4, 0, 1, ['expand','fill'], 'shrink', 0, 0);
4066$ComboBox_Lang_5a->show;
4067
4068#-----------------------table5b-----------------------#
4069my $ComboBox_NewLang = Gtk2::ComboBox->new_text;
4070foreach ( $langs{name060} , $langs{name061} ) { $ComboBox_NewLang->append_text ($_); }
4071$ComboBox_NewLang->set_active(0);
4072$ComboBox_NewLang->set_size_request( 92, -1 );
4073$ComboBox_NewLang->signal_connect("changed", \&add_new_lang, $ComboBox_NewLang);
4074$table5b->attach ($ComboBox_NewLang, 0, 1, 0, 1, ['expand','fill'], 'shrink', 0, 0);
4075$ComboBox_NewLang->show;
4076
4077# Create the Entry to add new language
4078my $entry_lang = new Gtk2::Entry();
4079$entry_lang->set_text( "" );
4080$entry_lang->signal_connect("changed",\&ative_save_lang);
4081$tooltips->set_tip( $entry_lang, $langs{name062} ) if $show_all_tooltips;
4082$table5b->attach ($entry_lang, 1, 2, 0, 1, 'fill', 'shrink', 0, 0);
4083#$entry_lang->set_sensitive($false);
4084$entry_lang->hide;
4085
4086my $button_save_lang = Gtk2::Button->new;
4087$button_save_lang->add( DrawIcons('gtk-save','button') );
4088$button_save_lang->signal_connect( "clicked", \&save_lang_button );
4089$button_save_lang->set_sensitive($false);
4090$button_save_lang->set( 'focus-on-click' => $false, 'relief' => 'none' );
4091$tooltips->set_tip( $button_save_lang, $langs{tip504} ) if $show_all_tooltips;
4092$table5b->attach ($button_save_lang, 2, 3, 0, 1, 'shrink', 'shrink', 0, 0);
4093$button_save_lang->show;
4094
4095
4096my $ComboBox_Lang_5b = Gtk2::ComboBox->new_text;
4097foreach (sort keys %all_language) { $ComboBox_Lang_5b->append_text ($_); }
4098
4099$pos = 0;
4100foreach my $key (sort keys %all_language) { if ($key eq $language) {$ComboBox_Lang_5b->set_active($pos); last;} $pos++; }
4101
4102$ComboBox_Lang_5b->set_size_request( 92, -1 );
4103$ComboBox_Lang_5b->signal_connect("changed", \&lang_choice5b);
4104$tooltips->set_tip($ComboBox_Lang_5b, $langs{tip505} ) if $show_all_tooltips;
4105$table5b->attach ($ComboBox_Lang_5b, 3, 4, 0, 1, ['expand','fill'], 'shrink', 0, 0);
4106$ComboBox_Lang_5b->show;
4107
4108#-------------------textview_5a----------------------#
4109# Construct a textview to put Guidance Language
4110my $textview_5a = Gtk2::TextView->new;
4111$textview_5a->set (         'editable' => $false,
4112                      'cursor_visible' => $false,
4113		         'left_margin' => 0,
4114			'right_margin' => 0,
4115                           'wrap-mode' => 'word');
4116
4117$textview_5a->set_border_width (4);
4118my $color_5a_base = Gtk2::Gdk::Color->parse ("#E6EDBD");
4119$textview_5a->modify_base ('normal', $color_5a_base);
4120my $color_5a_bg = Gtk2::Gdk::Color->parse ("#A0B8A3");
4121$textview_5a->modify_bg ('normal', $color_5a_bg);
4122$tooltips->set_tip($textview_5a, $langs{tip503} ) if $show_all_tooltips;
4123$table5a->attach ($textview_5a, 0, 4, 1, 2, ['expand','fill'], ['expand','fill'], 0, 0);
4124$textview_5a->show;
4125
4126# $buffer = Gtk2::TextBuffer->new; # see <man Gtk2::TextBuffer>
4127my $buffer_5a = $textview_5a->get_buffer;
4128$buffer_5a->create_tag ("italic_about",      'foreground' => "black",   # see <man Gtk2::TextTag> for more options
4129                                            #'background' => "yellow",
4130                                            justification => 'left',
4131                                                     font => "Sans",
4132				            'size-points' => "14");
4133
4134#-------------------textview_5b----------------------#
4135# Construct a textview to put Guidance Language
4136my $textview_5b = Gtk2::TextView->new;
4137$textview_5b->set (         'editable' => $true,
4138                      'cursor_visible' => $true,
4139		         'left_margin' => 0,
4140			'right_margin' => 0,
4141                           'wrap-mode' => 'word');
4142
4143$textview_5b->set_border_width (4);
4144my $color_5b_base = Gtk2::Gdk::Color->parse ("#E6EDBD");
4145$textview_5b->modify_base ('normal', $color_5b_base);
4146my $color_5b_bg = Gtk2::Gdk::Color->parse ("#A0B8A3");
4147$textview_5b->modify_bg ('normal', $color_5b_bg);
4148$table5b->attach ($textview_5b, 0, 4, 1, 2, ['expand','fill'], ['expand','fill'], 0, 0);
4149$textview_5b->show;
4150
4151# $buffer = Gtk2::TextBuffer->new; # see <man Gtk2::TextBuffer>
4152my $buffer_5b = $textview_5b->get_buffer;
4153$buffer_5b->signal_connect( 'changed', \&ative_save_lang );
4154$buffer_5b->create_tag ("version",           'foreground' => "blue",
4155                                            justification => 'left',
4156					             font => "Sans",
4157					    'size-points' => "14");
4158
4159#----------------------------------------------------#
4160
4161sub show_textview_language{
4162   lang_choice5a();
4163   lang_choice5b();
4164}
4165
4166sub lang_choice5a {
4167    my $entry=$ComboBox_Lang_5a->get_active_text;
4168    $language = $entry if defined $entry;  #print "111a-->\$language = $language \n";
4169    set_language();
4170    $tooltips->set_tip($ComboBox_Lang_5a, $langs{tip502});
4171    #$ComboBox_NewLang->set_popdown_strings ( $langs{name060} , $langs{name061} );
4172    show_textview_language_5a($ComboBox_Names);  #print "222a-->\$language = $language \n";
4173}
4174
4175sub lang_choice5b {
4176    my $entry=$ComboBox_Lang_5b->get_active_text;
4177    $language = $entry if defined $entry;  #print "111b-->\$language = $language \n";
4178    set_language();
4179    $tooltips->set_tip($ComboBox_Lang_5b, $langs{tip505}) if $show_all_tooltips;
4180    show_textview_language_5b($ComboBox_Names);  #print "222b-->\$language = $language \n";
4181}
4182
4183sub show_textview_language_5a {
4184    my ($combo_copy) = @_;
4185    my $key=$combo_copy->get_active_text;
4186
4187    $textview_5a->get_buffer->delete($textview_5a->get_buffer->get_bounds); #clear
4188    my $iter_5a = $buffer_5a->get_iter_at_offset (0); # get start of buffer
4189    $buffer_5a->insert_with_tags_by_name ($iter_5a, $langs{$key}, "italic_about");
4190}
4191show_textview_language_5a($ComboBox_Names);
4192
4193sub show_textview_language_5b {
4194    my ($combo_copy) = @_;
4195    my $key=$combo_copy->get_active_text;
4196
4197    $textview_5b->get_buffer->delete($textview_5b->get_buffer->get_bounds); #clear
4198    my $iter_5b = $buffer_5b->get_iter_at_offset (0); # get start of buffer
4199    $buffer_5b->insert_with_tags_by_name ($iter_5b, $langs{$key}, "version");
4200}
4201show_textview_language_5b($ComboBox_Names);
4202
4203sub add_new_lang {
4204    $entry_lang->hide if ( $ComboBox_NewLang->get_active == 0 );
4205    $entry_lang->show if ( $ComboBox_NewLang->get_active == 1 );
4206    #print "------> \$ComboBox_NewLang->get_active = ",$ComboBox_NewLang->get_active," \n";
4207}
4208
4209sub ative_save_lang {
4210   $button_save_lang->set_sensitive($true); # if ($textview_5b->get_buffer->get_modified);
4211}
4212
4213sub save_lang_button {
4214   my $key_names = $ComboBox_Names->get_active_text;
4215
4216   if ( $ComboBox_NewLang->get_active == 1 and $entry_lang->get_text eq "" ){
4217      $status_bar->push($context_id, $langs{msg095} );
4218      return $false;
4219   }
4220   $language = $ComboBox_Lang_5b->get_active_text; # print "--> Old \$language = $language \n";
4221   my $new_lang = $language;
4222
4223   if ( $ComboBox_NewLang->get_active == 1 and $entry_lang->get_text ne "" ){
4224      $new_lang = $entry_lang->get_text;  # print "\$new_lang = $new_lang\n";
4225
4226      $ComboBox_Lang_5a->append_text ($new_lang) if not $all_language{"$new_lang"};
4227      $ComboBox_Lang_5b->append_text ($new_lang) if not $all_language{"$new_lang"};
4228      $all_language{"$new_lang"} = $true;
4229   }
4230   set_language();
4231
4232   # Usage: Gtk2::TextBuffer::get_text(buffer, start, end, include_hidden_chars)
4233   $langs{$key_names} = $textview_5b->get_buffer->get_text($textview_5b->get_buffer->get_bounds,$true);
4234
4235   my $lang_file = "$lang_dir/${new_lang}.txt";
4236   open( OUTPUT, '>:utf8', filename_from_unicode $lang_file ) or die "Couldn't open <$lang_file> for writing: $!\n";
4237
4238      #foreach my $key ( sort keys %langs ) { ( my $value  = $langs{$key}) =~ s/\"/\\\"/g; print OUTPUT '$langs{',$key,'} = "',$value,"\";\n"; }
4239      foreach my $key ( sort keys %langs ) { print OUTPUT $key, '<===>', $langs{$key}, "\n"; }
4240
4241   close( OUTPUT );
4242
4243   print $key_names, ' = ', $langs{$key_names}, "\n";
4244   print "--> \"$lang_file\" \n";
4245   $status_bar->push($context_id, "--> \"$lang_file\"");
4246   $button_save_lang->set_sensitive($false);
4247}
4248
4249
4250###---------------------- final --------------------###
4251###--------------------- frame5 --------------------###
4252
4253###--------------------- frame6 --------------------###
4254###--------------------- in�cio --------------------###
4255# Adicionamos este frame6 em vbox6 (vbox6 est� dentro do notebook)
4256my $frame6 = new Gtk2::Frame();
4257# Set the frame's label
4258# Align the label at the right of the frame
4259$frame6->set_label_align( 1.0, 0.0 );
4260# Set the style of the frame
4261$frame6->set_shadow_type( 'etched_out' );
4262# $vbox->pack_start( $child, $expand, $fill, $padding );
4263$vbox6->pack_start( $frame6, $true, $true, 0 );
4264#$vbox6->add($frame6);
4265$frame6->show();
4266
4267###-------------------------------------------------###
4268
4269###------------------frame_normalize----------------###
4270
4271# we put this $frame_normalize inside the vbox_up
4272# Adicionamos este $frame_normalize em vbox_up ;; Este frame cont�m o bot�o normalize
4273my $frame_normalize = new Gtk2::Frame();
4274$frame_normalize->set_label_align( 1.0, 0.0 );
4275$frame_normalize->set_shadow_type( 'etched_out' );
4276#$box->pack_start ($child, $expand, $fill, $padding)
4277$vbox_up->pack_start( $frame_normalize, $false, $true, 0 );
4278#$vbox_up->add( $frame_normalize );
4279$frame_normalize->show();
4280
4281# adicionamos um hbox dentro do $frame_normalize
4282# Gtk2::HBox->new ($homogeneous=0, $spacing=5)
4283my $hbox_normalize = new Gtk2::HBox ( $false, 2 );
4284$hbox_normalize->set_border_width(2);
4285$frame_normalize->add($hbox_normalize);
4286$hbox_normalize->show;
4287
4288###----------------frame_prog_bar--------------------###
4289# we put this frame_prog_bar inside the vbox_up ;; This frame contain the Progress Bar
4290# Adicionamos este frame em vbox_up ;; Este frame cont�m os 'progress bar'
4291$frame_prog_bar = new Gtk2::Frame();
4292# Set the style of the frame
4293$frame_prog_bar->set_shadow_type( 'etched_out' );
4294#$box->pack_start ($child, $expand, $fill, $padding)
4295$vbox_up->pack_start( $frame_prog_bar, $false, $true, 0 );
4296$frame_prog_bar->show ;
4297
4298#adicionamos uma tabela dentro do frame_prog_bar
4299#$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
4300$table_down = new Gtk2::Table (3, 4, $false);
4301$table_down->set_border_width (4);
4302$table_down->set_row_spacings(4);
4303$table_down->set_col_spacings(2);
4304#$table_down->set_size_request( 510, 90 ); # width, height
4305$frame_prog_bar->add( $table_down );
4306$table_down->show;
4307
4308###------------------frame_debug----------------------###
4309# we put this frame_debug inside the vbox_down
4310# Adicionamos este frame em vbox_down
4311# Este frame cont�m os comandos utilizados , debug
4312$frame_debug = new Gtk2::Frame();
4313$frame_debug->set_label_align( 1.0, 0.0 );
4314$frame_debug->set_shadow_type( 'etched_out' );
4315#$box->pack_start ($child, $expand, $fill, $padding)
4316$vbox_down->pack_start( $frame_debug, $true, $true, 0 );
4317#$frame_debug->show;
4318
4319## --------------------- Statusbar ------------------- ##
4320$status_bar = Gtk2::Statusbar->new;
4321$vbox_main->pack_start( $status_bar, $false, $true, 0 );
4322$status_bar->set_has_resize_grip ($true);
4323#$status_bar->signal_connect ( button_press_event => \&event_bar_button );
4324$status_bar->show;
4325
4326sub event_bar_button{  # not used, only for test
4327   my $widget = shift;	  # Gtk2::Statusbar
4328   my $event = shift;	  # GdkEventButton *event  : see Gtk2::Gdk::Event
4329
4330   #print "fora \n";
4331   if ($event->button == 1) {
4332      #print "button 1\n";
4333      $table3->set_size_request( ($window->allocation->width) * 0.70    ,-1 ); # width (70%), height;
4334      my $height_fd = $frame_debug->allocation->height;
4335      print "height_fd = $height_fd  \n";
4336   }
4337}
4338
4339###----------------------------------------------------###
4340####################----------------######################
4341##------------------------------------------------------##
4342
4343#--------------------- I c o n s ----------------------#
4344
4345my @items = ( { stock_id => "gtk-apply", label => "_unselect" },
4346              { stock_id => "gtk-go-down", label => ""    },
4347	      { stock_id => "gtk-open", label => ""       },
4348	      { stock_id => "gtk-add",   label => ""      },
4349	      { stock_id => "gtk-go-up", label => ""      },
4350	      { stock_id => "gtk-cdrom", label => ""      },
4351	      { stock_id => "gtk-close", label => ""      },
4352	      { stock_id => "gtk-cancel", label => ""     },
4353	      { stock_id => "gtk-refresh", label => ""    },
4354	      { stock_id => "gtk-go-back", label => ""    },
4355	      { stock_id => "gtk-go-forward", label => "" },
4356	      { stock_id => "gtk-goto-first", label => "" },
4357	      { stock_id => "gtk-goto-last", label => ""  },
4358	      { stock_id => "gtk-goto-bottom", label => "_Show"    },
4359	      { stock_id => "gtk-goto-top", label => "_Hide"       },
4360              { stock_id => "gtk-yes", label => "_normalize", },
4361	    );
4362Gtk2::Stock->add (@items);           # Register our stock items
4363#print Gtk2::Stock->list_ids;
4364
4365###--------------------------------------------------###
4366
4367#####------------------T a b l e 1 ------------------#####
4368# Now, we put the widgets in table1
4369
4370# criar um label (etiqueta - r�tulo) ao lado do menu
4371$label = new Gtk2::Label( $langs{name001} . ":");
4372$label->set_alignment( 1.0, 0.5 );  # right
4373$table1->attach ($label, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
4374$label->show();
4375
4376#####------------ start of ComboBox ----------#####
4377my @normalize_type = ( 'Track', 'Album', 'None' );
4378
4379my $ComboBox_Norm_Type = Gtk2::ComboBox->new_text;
4380foreach my $type (@normalize_type) { $ComboBox_Norm_Type->append_text ($type); }
4381
4382$pos = 0;
4383#$ComboBox_Norm_Type->set_active($pos); # set default value: $norm_type = 'Track';
4384foreach my $type (@normalize_type) { if ($type eq $norm_type) {$ComboBox_Norm_Type->set_active($pos); last;} $pos++; }
4385
4386#$ComboBox_Norm_Type->set_size_request( 92, -1 );
4387$ComboBox_Norm_Type->signal_connect("changed", \&change_norm_type);
4388$tooltips->set_tip( $ComboBox_Norm_Type, $langs{tip101} ) if $show_all_tooltips;
4389$table1->attach ($ComboBox_Norm_Type,  1, 2, 0, 1, 'fill', 'shrink', 0, 0);
4390$ComboBox_Norm_Type->show;
4391
4392sub change_norm_type {
4393   my ($ComboBox_copy) = @_;
4394   $norm_type = $ComboBox_copy->get_active_text;
4395
4396   if ($norm_type eq "Track" ) {$spinner->set_sensitive($true );}
4397   if ($norm_type eq "Album" ) {$spinner->set_sensitive($false);}
4398   if ($norm_type eq "None"  ) {$spinner->set_sensitive($false);}
4399   return $norm_type;
4400}
4401
4402#####------------ final of ComboBox ----------#####
4403
4404# Create a centering alignment object;
4405$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
4406$table1->attach_defaults ($align,  2, 3, 0, 1);
4407$align->show;
4408
4409# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
4410my $adj = new Gtk2::Adjustment( $gain, -12.00, 12.00, 0.01, 0, 0 );
4411#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
4412$spinner = new Gtk2::SpinButton( $adj, 5, 2 );
4413$spinner->set_wrap( $true );
4414$spinner->set_editable($true);
4415$spinner->set_numeric ($true);
4416$spinner->set_update_policy( 'if_valid' );
4417$tooltips->set_tip( $spinner, $langs{tip102} ) if $show_all_tooltips;
4418$align->add($spinner);
4419$spinner->show;
4420
4421#-------------------------------------#
4422
4423change_norm_type($ComboBox_Norm_Type);
4424
4425#-------------------------------------#
4426
4427$label = new Gtk2::Label( $langs{name002} . ":" );
4428$label->set_alignment( 1.0, 0.5 );  # right
4429$table1->attach ($label, 3, 4, 0, 1, 'fill', 'shrink', 0, 0);
4430$label->show();
4431
4432$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
4433$table1->attach ($align,  4, 5, 0, 1, 'fill', 'shrink', 0, 0);
4434$align->show;
4435
4436# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
4437$insensibility = number_value($insensibility);
4438$adj = new Gtk2::Adjustment( $insensibility, 0.2, 5.0, 0.1, 0, 0 );
4439#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
4440$spinner_sensi = new Gtk2::SpinButton( $adj, 0.2, 1 );
4441$spinner_sensi->set_wrap( $false );
4442$spinner_sensi->set_editable($false);
4443$spinner_sensi->set_update_policy( 'if_valid' );
4444$tooltips->set_tip( $spinner_sensi, $langs{tip103} ) if $show_all_tooltips;
4445$align->add($spinner_sensi);
4446$spinner_sensi->show();
4447
4448$label = new Gtk2::Label( "dB" );
4449$label->set_alignment( 0.0, 0.5 );  # right
4450$table1->attach ($label, 5, 6, 0, 1, 'fill', 'shrink', 0, 0);
4451$label->show();
4452
4453# criar um label (etiqueta - r�tulo) ao lado do menu
4454$label = new Gtk2::Label( $langs{name003} . ":" );
4455$label->set_alignment( 1.0, 0.5 );  # right
4456$table1->attach ($label, 0, 1, 1, 2, 'fill', 'fill', 0, 0);
4457$label->show();
4458
4459# Create the Entry to put the file to normalize
4460$entry_f = new Gtk2::Entry( );
4461#$entry_f->set_max_length (100);
4462$entry_f->set_text( " " );
4463$tooltips->set_tip( $entry_f, $langs{msg063} ) if $show_all_tooltips;
4464$entry_f->select_region( 0, length( $entry_f->get_text() ) );
4465$table1->attach_defaults ($entry_f, 1, 6, 1, 2 );
4466$entry_f->set_sensitive($false);
4467$entry_f->set('editable' => $false );
4468$entry_f->show();
4469
4470#--------------- Input Directory -----------------#
4471
4472$label = new Gtk2::Label( $langs{name004} . ":" );
4473$label->set_alignment( 1.0, 0.5 );  # right
4474$table1->attach ($label, 0, 1, 2, 3, 'fill', 'fill', 0, 0);
4475$label->show();
4476
4477# Create the Entry to put the directory path
4478$entry_dir_input = new Gtk2::Entry( );
4479#$entry_dir_input->set_max_length (100);
4480
4481$directory = $ENV{'PWD'};                               # start value
4482eval { $directory = filename_to_unicode $directory; };  # convert filename to unicode
4483$directory =~ s/\/{1,}$//;                              # remove the '/' character from final if it exists.
4484$directory_base = $directory;                           # used to find 'directory_ramain' with remove_directory_base()
4485
4486$entry_dir_input->set_text( $directory );
4487$tooltips->set_tip( $entry_dir_input, "$directory") if $show_all_tooltips;
4488$entry_dir_input->select_region( 0, length( $entry_dir_input->get_text() ) );
4489$table1->attach_defaults ($entry_dir_input, 1, 5, 2, 3 );
4490$entry_dir_input->set_sensitive($false);
4491$entry_dir_input->set('editable' => $false );
4492$entry_dir_input->show;
4493
4494# Create Dir_Selection button
4495#$button = Gtk2::Button->new_from_stock ('gtk-open');
4496$button = Gtk2::Button->new;
4497$button->add( DrawIcons('gtk-open','button') );  # 'button' is the size
4498
4499$tooltips->set_tip( $button, $langs{tip104} ) if $show_all_tooltips;
4500$button->signal_connect( "clicked", \&dir_selection );
4501$table1->attach ($button,  5, 6, 2, 3, 'shrink', 'shrink', 0, 0);
4502$button->show_all;
4503# This makes it so the button is the default.
4504$button->can_default($true);
4505# This grabs this button to be the default button. Simply hitting the "Enter"
4506# key will cause this button to activate.
4507$button->grab_default;
4508$button->set( 'focus-on-click' => $false, 'relief' => 'none' );
4509
4510#--------------- Output Directory -----------------#
4511
4512$label = new Gtk2::Label( $langs{name052} . ":" );
4513$label->set_alignment( 1.0, 0.5 );  # right
4514$table1->attach ($label, 0, 1, 3, 4, 'fill', 'fill', 0, 0);
4515$label->show();
4516
4517my $entry_dir_output = new Gtk2::Entry();
4518if ( ! -e filename_from_unicode $directory_output ){ $directory_output = $home;}
4519$entry_dir_output->set_text( $directory_output );
4520$tooltips->set_tip( $entry_dir_output, $directory_output ) if $show_all_tooltips;
4521$entry_dir_output->set('editable' => $false );
4522$table1->attach_defaults ($entry_dir_output, 1, 5, 3, 4 );
4523$entry_dir_output->show;
4524
4525$button = Gtk2::Button->new;
4526$button->add( DrawIcons('gtk-go-down','button') );
4527$tooltips->set_tip( $button, $langs{tip105} ) if $show_all_tooltips;
4528$button->signal_connect( "clicked", \&dir_selection_output);
4529$button->set( 'focus-on-click' => $false, 'relief' => 'none' );
4530$table1->attach ($button,  5, 6, 3, 4, 'shrink', 'shrink', 0, 0);
4531$button->show;
4532
4533#----------- Scrolling Label Viewport -------------#
4534#my $viewport_directory_input = scrolling_label_viewport( $directory, 60, 2);
4535#$table1->attach ( $viewport_directory_input, 1, 5, 2, 3, ['expand','fill'], 'shrink', 0, 0);
4536
4537#$table1->remove( $viewport_directory_output );
4538#$viewport_directory_output = scrolling_label_viewport( $directory_output, 60, 2);
4539#$table1->attach( $viewport_directory_output, 1, 5, 3, 4, ['expand','fill'], 'shrink', 0, 0);
4540
4541my $timer_label; my $hadj;
4542sub scrolling_label_viewport { # not used
4543   my ($text, $interval, $incremental) = @_;
4544
4545   # $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
4546   $hadj = new Gtk2::Adjustment( 5, 1.0, 30.0, 1.0, 1.0, 50.0 );
4547
4548   my $label = Gtk2::Label->new( $text );
4549   $label->set_alignment(0.0, 0.5);
4550   $label->show;
4551
4552   # See <man Gtk2::Viewport> and Gtk2::Adjustment
4553   #widget = Gtk2::Viewport->new ($hadjustment=undef, $vadjustment=undef)
4554   my $viewport = Gtk2::Viewport->new($hadj, undef);
4555   $viewport->set_shadow_type('out');
4556   $viewport->add ($label);
4557   $viewport->set_size_request(20,-1);
4558   $viewport->signal_connect ( 'enter-notify-event' => sub {
4559      if ( defined($timer_label) ){ Glib::Source->remove($timer_label);}  # to not duplicate the MainLoop
4560      $timer_label = Glib::Timeout->add ($interval, \&scroll_label, $incremental);      # see <man Glib::MainLoop>
4561   });
4562   $viewport->signal_connect ( 'leave_notify_event' => sub { if ( defined $timer_label ){ Glib::Source->remove($timer_label); } } );
4563   $viewport->set_events ([ @{ $viewport->get_events }, 'exposure-mask', 'enter-notify-mask', 'leave-notify-mask'] );
4564   $viewport->show;
4565   return $viewport; #widget
4566}
4567
4568my $sign = 1; my $i=1;
4569sub scroll_label {
4570   my $incremental = shift;
4571   my $upper = $hadj->upper;
4572   my $page_size = $hadj->page_size;
4573   my $offset = $upper - $page_size;
4574   if ( $offset <= 0 ){ return; }
4575   my $x=$hadj->get_value;
4576
4577   if ( $x <= 0 or $x >= $offset ) { $sign = - $sign; }
4578   $i = $i + $sign * $incremental;
4579
4580   $hadj->set_value($i);
4581   #print "x = $x ; sign = $sign ; offset = $offset ; upper = $upper ; page_size = $page_size\n";
4582   return $timer_label;
4583}
4584
4585# Output : Lossy Compression
4586# Output : Lossless Compression
4587# Output : Uncompressed
4588
4589
4590################-----Output--------################## Output : Lossy Compression
4591#######---------Lossy Compression-----------#########
4592
4593# veja:   man perlnumber  ou perlop ou man overload
4594
4595my $button_output_mp3 = new Gtk2::RadioButton(  undef , "mp3");
4596$tooltips->set_tip( $button_output_mp3, $langs{tip108} . "\n\n" . $langs{tip115} ) if $show_all_tooltips;
4597$table11->attach_defaults($button_output_mp3, 0, 1, 0, 1);
4598$button_output_mp3->signal_connect( "toggled", \&set_extension_output );
4599$button_output_mp3->show;
4600
4601my $button_output_mp4 = new Gtk2::RadioButton( $button_output_mp3, "mp4");
4602$tooltips->set_tip( $button_output_mp4, $langs{tip109} . "\n\n" . $langs{tip115} ) if $show_all_tooltips;
4603$table11->attach_defaults($button_output_mp4, 1, 2, 0, 1);
4604$button_output_mp4->signal_connect( "toggled", \&set_extension_output );
4605$button_output_mp4->show;
4606
4607my $button_output_mpc = new Gtk2::RadioButton( $button_output_mp4, "mpc");
4608$tooltips->set_tip( $button_output_mpc, $langs{tip110} . "\n\n" . $langs{tip115} ) if $show_all_tooltips;
4609$table11->attach_defaults($button_output_mpc, 0, 1, 1, 2);
4610$button_output_mpc->signal_connect( "toggled", \&set_extension_output );
4611$button_output_mpc->show;
4612
4613my $button_output_ogg = new Gtk2::RadioButton( $button_output_mpc, "ogg");
4614$tooltips->set_tip( $button_output_ogg, $langs{tip111} . "\n\n" . $langs{tip115} ) if $show_all_tooltips;
4615$table11->attach_defaults($button_output_ogg, 1, 2, 1, 2);
4616$button_output_ogg->signal_connect( "toggled", \&set_extension_output );
4617$button_output_ogg->show;
4618
4619################-----Output--------################## Output : Lossless Compression
4620#######-------Lossless Compression----------#########
4621
4622# see /usr/share/doc/perl-Gtk2-doc/examples/assistant.pl for RadioButton
4623
4624my $button_output_ape = new Gtk2::RadioButton( $button_output_ogg, "ape");
4625$tooltips->set_tip( $button_output_ape, $langs{tip112} . "\n\n" . $langs{tip116} ) if $show_all_tooltips;
4626$table12->attach_defaults($button_output_ape, 0, 1, 0, 1);
4627$button_output_ape->signal_connect( "toggled", \&set_extension_output );
4628$button_output_ape->show;
4629
4630my $button_output_flac = new Gtk2::RadioButton( $button_output_ape, "flac");
4631$tooltips->set_tip( $button_output_flac, $langs{tip113} . "\n\n" . $langs{tip116} ) if $show_all_tooltips;
4632$table12->attach_defaults($button_output_flac, 1, 2, 0, 1);
4633$button_output_flac->signal_connect( "toggled", \&set_extension_output );
4634$button_output_flac->show;
4635
4636################-----Output--------################## Output : Uncompressed
4637#######------------Uncompressed-------------#########
4638
4639my $button_output_wav = new Gtk2::RadioButton( $button_output_flac, "wav");
4640$tooltips->set_tip( $button_output_wav, $langs{tip114} ) if $show_all_tooltips;
4641$table13->attach_defaults($button_output_wav, 0, 1, 0, 1);
4642$button_output_wav->signal_connect( "toggled", \&set_extension_output );
4643$button_output_wav->show;
4644
4645##----------------------------------------------------##
4646#####----------------T a b l e 2 ------------------#####
4647
4648$check_button_del_wav = Gtk2::CheckButton->new ( $langs{name009} );
4649$check_button_del_wav->set_active($delete_wave);
4650$tooltips->set_tip( $check_button_del_wav, $langs{tip201} ) if $show_all_tooltips;
4651#$check_button_del_wav->set_alignment (6,0);
4652$table2->attach($check_button_del_wav,  0, 1, 0, 1, ['expand','fill'], 'shrink', 0, 0);
4653$check_button_del_wav->show;
4654
4655$check_button_tooltips = Gtk2::CheckButton->new ($langs{name057});
4656$check_button_tooltips->set_active($show_all_tooltips);
4657$tooltips->set_tip( $check_button_tooltips, $langs{tip204} ."\n". $langs{tip502} );
4658$table2->attach($check_button_tooltips,  1, 2, 0, 1, 'fill', 'shrink', 0, 0);
4659$check_button_tooltips->show;
4660
4661
4662$label = new Gtk2::Label( $langs{name012} . ":" );
4663$label->set_alignment( 1.0, 0.5 );
4664$table2->attach ($label, 2, 3, 0, 1, ['fill','expand'], 'shrink', 0, 0);
4665$label->show();
4666
4667$adj = new Gtk2::Adjustment( $priority, 0, 19, 1, 0, 0 );
4668$spinner_pri = new Gtk2::SpinButton( $adj, 0.1, 0 );
4669$spinner_pri->set_editable($false);
4670$spinner_pri->set_size_request( 72, -1 );
4671$spinner_pri->set_update_policy( 'if_valid' );
4672$spinner_pri->set_wrap( $true );
4673$tooltips->set_tip( $spinner_pri, $langs{tip206} ) if $show_all_tooltips;
4674$table2->attach($spinner_pri,  3, 4, 0, 1, ['expand','fill'], 'shrink', 0, 0);
4675$spinner_pri->show();
4676
4677$check_change_properties = Gtk2::CheckButton->new ($langs{name010});
4678$check_change_properties->set_active($change_properties);
4679$check_change_properties->signal_connect( "clicked", \&encode_choice_sensitive );
4680$tooltips->set_tip( $check_change_properties, $langs{tip202} ) if $show_all_tooltips;
4681$table2->attach($check_change_properties,  0, 1, 1, 2, 'fill', 'fill', 0, 0);
4682$check_change_properties->show;
4683
4684$check_button_animation = Gtk2::CheckButton->new ( $langs{name055} );
4685$check_button_animation->set_active($show_tux_animation);
4686$check_button_animation->signal_connect( "clicked", \&tux_animation_textview );
4687$tooltips->set_tip( $check_button_animation, $langs{tip205} ) if $show_all_tooltips;
4688$table2->attach($check_button_animation, 1, 2, 1, 2, ['expand','fill'], 'shrink', 0, 0);
4689$check_button_animation->show;
4690
4691#####---------------------------------------------#####
4692
4693$check_button_overwrite = Gtk2::CheckButton->new ($langs{name011});
4694$check_button_overwrite->set_active($overwrite);
4695$tooltips->set_tip( $check_button_overwrite, $langs{tip203} ) if $show_all_tooltips;
4696$table2->attach($check_button_overwrite,  0, 1, 2, 3, 'fill', 'fill', 0, 0);
4697$check_button_overwrite->show;
4698
4699#####---------------------------------------------#####
4700
4701$label = new Gtk2::Label( $langs{name014} . ":" );
4702$label->set_alignment( 1.0, 0.5 );
4703$table2->attach ($label, 2, 3, 1, 2, ['fill','expand'], 'shrink', 0, 0);
4704$label->show;
4705
4706$entry_format_cda = new Gtk2::Entry( );
4707$entry_format_cda->set_text( $file_format_cda_rip );
4708$tooltips->set_tip( $entry_format_cda, $langs{tip207} ) if $show_all_tooltips;
4709$table2->attach($entry_format_cda,  3, 4, 1, 2, ['fill','expand'], 'shrink', 0, 0);
4710$entry_format_cda->show;
4711
4712#####---------------------------------------------#####
4713
4714$label = new Gtk2::Label( "Comment Tag" . ":" );
4715$label->set_alignment( 1.0, 0.5 );
4716$table2->attach ($label, 2, 3, 2, 3, ['fill','expand'], 'shrink', 0, 0);
4717$label->show;
4718
4719$entry_def_comment = new Gtk2::Entry; # see 'sub normalize'
4720$entry_def_comment->set_text( $comment_tag );
4721$tooltips->set_tip( $entry_def_comment, $langs{tip250} ) if $show_all_tooltips;
4722$table2->attach($entry_def_comment,  3, 4, 2, 3, ['fill','expand'], 'shrink', 0, 0);
4723$entry_def_comment->show;
4724
4725##----------------------------------------------------##
4726#####---------------T a b l e 202 -----------------#####
4727
4728#####--------------------Combo---------------------#####
4729
4730# criar um label (etiqueta - r�tulo) ao lado do menu
4731$label = new Gtk2::Label( $langs{name015} . ":" );
4732$label->set_alignment( 1.0, 0.5 );
4733$table202->attach ($label, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
4734$label->show();
4735
4736my @all_encodings = Encode->encodings(":all"); # see <man Encode>
4737#print ("Encode list = @all_encodings\n");
4738
4739$ComboBox_char = Gtk2::ComboBox->new_text;
4740#foreach (@all_encodings) { $ComboBox_char->append_text ($_); }
4741#$pos = 0; foreach my $key (@all_encodings) { if ($key eq $id3tag_character) {$ComboBox_char->set_active($pos); last;} $pos++; }
4742ComboBox_fill_with_array(\$ComboBox_char,\@all_encodings);
4743ComboBox_select_this_text(\$ComboBox_char,\@all_encodings,\$id3tag_character);
4744$ComboBox_char->set_size_request( 90, -1 );
4745$ComboBox_char->signal_connect("changed", \&ComboBox_choice,\$id3tag_character);
4746$table202->attach ($ComboBox_char, 1, 2, 0, 1, ['expand','fill'], 'shrink', 0, 0);
4747$tooltips->set_tip( $ComboBox_char, $langs{tip229} ) if $show_all_tooltips;  # "Used to encode ID3v2.3.0 Tag for MP3 files."
4748$ComboBox_char->show;
4749
4750sub ComboBox_choice {   # see 'sub player_choice'
4751   my ($ComboBox_copy, $scalar_ref) = @_;
4752   my $char = $ComboBox_copy->get_active_text;
4753
4754   $$scalar_ref = $char;
4755   #print "\nComboBox_copy = $ComboBox_copy ;; \n\$scalar_ref = $scalar_ref ;; scalar_ref value = $$scalar_ref \n";
4756}
4757
4758sub ComboBox_fill_with_array {
4759   my ($ref_ComboBox, $ref_array) = @_;
4760   $$ref_ComboBox->remove_text(0) while ( $$ref_ComboBox->get_active >= 0 ); # remove all old values
4761   $$ref_ComboBox->append_text($_) foreach (@$ref_array);
4762}
4763
4764sub ComboBox_select_this_text {
4765   my ($ref_ComboBox, $ref_array, $ref_val) = @_;            # the selected text is $ref_val
4766   return if ( not defined $$ref_val or @$ref_array <= 0 );  # print "ref_val = $$ref_val \n";
4767
4768   my $index = 0;
4769   foreach my $key (@$ref_array) { if ($key eq $$ref_val) {$$ref_ComboBox->set_active($index); last;} $index++; }
4770
4771   # if no one selection, then select the first array element
4772   if ($$ref_ComboBox->get_active < 0 and @$ref_array > 0){
4773      $$ref_ComboBox->set_active(0);                  # print "antes  ref_val = $$ref_val \n";
4774      $$ref_val = $$ref_ComboBox->get_active_text;    # print "depois ref_val = $$ref_val \n";
4775   }
4776}
4777
4778#####-------------------final of Combo-------------#####
4779
4780#####--------------------Combo---------------------#####
4781
4782$label = new Gtk2::Label( $langs{name029} . ":" );
4783$label->set_alignment( 1.0, 0.5 );
4784$table202->attach ($label, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
4785$label->show;
4786
4787my @rippers = ( "cdparanoia", "cdda2wav" );
4788
4789$ComboBox_rip = Gtk2::ComboBox->new_text;
4790ComboBox_fill_with_array(\$ComboBox_rip,\@rippers);
4791#ComboBox_set_popdown_strings($ComboBox_rip,@rippers);
4792ComboBox_select_this_text(\$ComboBox_rip,\@rippers,\$ripper);
4793$ComboBox_rip->set_size_request( 90, -1 );
4794$ComboBox_rip->signal_connect("changed", \&ripperChanged);
4795$table202->attach ($ComboBox_rip, 1, 2, 1, 2, ['expand','fill'], 'shrink', 0, 0);
4796$ComboBox_rip->show;
4797
4798sub ripperChanged {
4799   my ($ComboBox_copy) = @_;
4800   $ripper = $ComboBox_copy->get_active_text;
4801
4802   if ($ripper eq "cdparanoia"){
4803      $notebook2->remove_page (3);
4804      $notebook2->append_page_menu ( $frame204, $label_ripper, undef  );
4805      change_font_for_all_child($window_font_name,$frame204);
4806   }
4807   if ($ripper eq "cdda2wav"){
4808      $notebook2->remove_page (3);
4809      $notebook2->append_page_menu ( $frame205, $label_ripper, undef  );
4810      change_font_for_all_child($window_font_name,$frame205);
4811   }
4812}
4813
4814#####-------------------final of Combo-------------#####
4815
4816#####--------------------Combo---------------------#####
4817
4818$label = new Gtk2::Label( $langs{name064} . ":" );
4819$label->set_alignment( 1.0, 0.5 );
4820$table202->attach ($label, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
4821$label->show;
4822
4823my @all_cdplayer;
4824
4825sub Make_ComboBox_cdplayer {   # See the cdplayer_available() subroutine to search the cdplayer
4826   my $ComboBox_cdplayer = Gtk2::ComboBox->new_text;
4827   ComboBox_fill_with_array(\$ComboBox_cdplayer,\@all_cdplayer);
4828   ComboBox_select_this_text(\$ComboBox_cdplayer,\@all_cdplayer,\$cdplayer);
4829   $ComboBox_cdplayer->signal_connect("changed", \&cdplayerChanged);
4830   $table202->attach ($ComboBox_cdplayer, 1, 2, 2, 3, ['expand','fill'], 'shrink', 0, 0);
4831   $ComboBox_cdplayer->show;
4832}
4833
4834sub cdplayerChanged {  ## choose cdplayer
4835   my ($ComboBox_copy) = @_;
4836   $cdplayer = $ComboBox_copy->get_active_text; # $cdplayer = "gnormalize::cdplay", "Audio::CD", "cdcd"
4837   # print "cdplayer = $cdplayer\n";
4838
4839   if ( $cdplayer eq "Audio::CD" and $use_audiocd ){ # see <man Audio::CD>
4840       $audiocd = Audio::CD->init("$audiodevice_path");
4841       $have_current_track = audio_cd_have_current_track($audiodevice_path);
4842   }
4843}
4844
4845#####-------------------final of Combo-------------#####
4846
4847#------------------------- Font -----------------------#
4848# criar um label (etiqueta - r�tulo) ao lado do menu
4849$label = new Gtk2::Label( $langs{name017} . ":" );
4850$label->set_alignment( 1.0, 0.5 );
4851$table202->attach ($label, 2, 3, 0, 1, 'fill', 'shrink', 0, 0);
4852$label->show;
4853
4854# Build the font-button
4855$button_font = Gtk2::Button->new($window_font_name);
4856$button_font->signal_connect( 'clicked', \&window_select_font );
4857#$button_font->signal_connect( 'clicked', \&gnome_font);
4858$table202->attach ($button_font, 3, 4, 0, 1, ['expand','fill'], 'shrink', 0, 0);
4859$tooltips->set_tip( $button_font, $langs{tip208} ) if $show_all_tooltips;
4860$button_font->set_sensitive($true);
4861$button_font->show;
4862
4863#------------------------------------------------------#
4864
4865# my $widget = Glib::Object->new(  veja histogram
4866my $window_bg_color = color_bg($window);
4867#print "window_bg_color = $window_bg_color \n"; #E6E7E6
4868
4869# return the background color of widget in hexdecimal format
4870sub color_bg {  # not used
4871   my ( $widget ) = @_;
4872   my $color;
4873   my $red;
4874   my $green;
4875   my $blue;
4876
4877   # see man Gtk2::Style
4878   $red = $widget->get_style->bg('normal')->red;
4879   $green = $widget->get_style->bg('normal')->green;
4880   $blue = $widget->get_style->bg('normal')->blue;
4881
4882   # Convert to HTML format.  There are better ways to do this.
4883   # uc: Returns an uppercased version of EXPR.
4884   # %x   an unsigned integer, in hexadecimal ; l: long
4885   $red   = uc( sprintf( "%02lx", $red/256 ) );  #print "red = $red\n";
4886   $green = uc( sprintf( "%02lx", $green/256 ) );
4887   $blue  = uc( sprintf( "%02lx", $blue/256 ) );
4888
4889   $color = "#$red$green$blue"; #   print "color = $color\n";  # like #E6E7E6
4890   return $color;
4891}
4892
4893sub window_select_font {
4894	my $widget = $label;
4895
4896	# repair the standard cancel label
4897        my @items = ( { stock_id => "gtk-cancel", label => "_Cancel"     } );
4898        Gtk2::Stock->add (@items);  # Register our stock items
4899
4900	my $fsd = Gtk2::FontSelectionDialog->new ('Font Selection Dialog');
4901	$fsd->set_position('mouse');
4902	$fsd->set_preview_text('abcdefghijk ABCDEFGHIJK 1234567890');
4903
4904	my $original_font_name = $widget->get_style->font_desc->to_string;
4905	$fsd->set_font_name ($original_font_name);
4906        #print "original_font_name = $original_font_name\n";
4907
4908	$fsd->signal_connect ('response' => sub {
4909		my (undef, $response) = @_;
4910		if ($response eq 'ok') {
4911		   my $font_name = $fsd->get_font_name;
4912		   return unless $font_name;
4913		   $button_font->set_label($font_name);
4914		   change_font_for_all_child($font_name, $window);
4915		   $window_font_name = $font_name;
4916	           #print "aquui  -> window_font_name = $window_font_name\n";
4917		}
4918		$fsd->destroy;
4919	});
4920
4921	$fsd->show;
4922}
4923
4924sub change_font_for_all_child {
4925  my $font_name = shift || 'Sans 10';
4926  my $container = shift || $window;
4927
4928  my $font_desc = Gtk2::Pango::FontDescription->from_string($font_name);
4929
4930  # notebook: notebook labels
4931  $label_data->modify_font($font_desc); $label_config->modify_font($font_desc);
4932  $label_info->modify_font($font_desc); $label_rip->modify_font($font_desc);
4933  $label_about->modify_font($font_desc);
4934
4935  # notebook2: notebook labels
4936  $label_option1->modify_font($font_desc); $label_option2->modify_font($font_desc);
4937  $label_cddb->modify_font($font_desc);    $label_ripper->modify_font($font_desc);
4938
4939  # TreeViewColumn : treeview_play
4940  my @cols = $treeview_play->get_columns; # Retuns an array of columns.
4941  foreach my $col (@cols){
4942     # list = $tree_column->get_cell_renderers ; see <man Gtk2::TreeViewColumn>
4943     my $renderer = $col->get_cell_renderers;
4944     next if ($renderer =~ /Toggle/);
4945     $renderer->set ('font' => $font_name);
4946  }
4947  $renderer_track->set  ('font' => $font_name, 'xalign' => 0.5) if $renderer_track;
4948  $renderer_artist->set ('font' => $font_name, style => 'italic', scale => '0.9') if $renderer_artist;
4949  $renderer_title->set  ('font' => $font_name, style => 'italic', scale => '0.9') if $renderer_title;
4950  $renderer_album->set  ('font' => $font_name, style => 'italic', scale => '0.9') if $renderer_album;
4951
4952  # TreeViewColumn : treeview_album
4953  $renderer_album_playlist->set ('font' => $font_name, style => 'italic', scale => '0.9')   if $renderer_album_playlist;
4954  $renderer_album_play_count->set ('font' => $font_name, style => 'italic', scale => '0.9') if $renderer_album_play_count;
4955  # TreeViewColumn : treeview_artist
4956  $renderer_artist_playlist->set ('font' => $font_name, style => 'italic', scale => '0.9')   if $renderer_artist_playlist;
4957  $renderer_artist_play_count->set ('font' => $font_name, style => 'italic', scale => '0.9') if $renderer_artist_play_count;
4958
4959  foreach my $child0 ($container->get_children) {
4960     if (not child_has_child ($font_desc, $child0) ) { next; }
4961     foreach my $child1 ($child0->get_children) {
4962        if (not child_has_child ($font_desc, $child1) ) { next; }
4963        foreach my $child2 ($child1->get_children) {
4964	   if (not child_has_child ($font_desc, $child2) ) { next; }
4965           foreach my $child3 ($child2->get_children) {
4966	      if (not child_has_child ($font_desc, $child3) ) { next; }
4967              foreach my $child4 ($child3->get_children) {
4968	         if (not child_has_child ($font_desc, $child4) ) { next; }
4969                 foreach my $child5 ($child4->get_children) {
4970		    if (not child_has_child ($font_desc, $child5) ) { next; }
4971                    foreach my $child6 ($child5->get_children) {
4972		       if (not child_has_child ($font_desc, $child6) ) { next; }
4973                       foreach my $child7 ($child6->get_children) {
4974		          if (not child_has_child ($font_desc, $child7) ) { next; }
4975                          foreach my $child8 ($child7->get_children) {
4976			     if (not child_has_child ($font_desc, $child8) ) { next; }
4977			     foreach my $child9 ($child8->get_children) {
4978			        if (not child_has_child ($font_desc, $child9) ) { next; }
4979                                $child9->modify_font($font_desc);
4980			     }
4981			  }
4982		       }
4983		    }
4984		 }
4985	      }
4986	   }
4987	}
4988     }
4989  }
4990
4991}
4992
4993sub child_has_child {
4994   my $font_desc = shift;
4995   my $child = shift;
4996   # Don't change the font of this widget child, so don't search for its child
4997   if ( $child =~ /(Gtk2::Label|Gtk2::SpinButton|Gtk2::Entry|Gtk2::HSeparator|Gtk2::AccelLabel|Gtk2::TextView)/i  or
4998        $child =~ /(Gtk2::HSeparator|Gtk2::ProgressBar|Gtk2::DrawingArea|Gtk2::Image|Gtk2::Arrow|Gtk2::ComboBox)/i ){
4999              $child->modify_font($font_desc);
5000              return $false;
5001   }
5002   return $true;
5003}
5004
5005sub gnome_font { # not more used
5006   exec_cmd_system2("gnome-font-properties");
5007   $change_fonte = $true;
5008   $change_fonte_norm = $true;
5009   $change_font_layout = $true;
5010   $change_font_rand = $true;
5011}
5012
5013# change_font_for_all_child($window_font_name); # change the font at the end
5014
5015##-------------------------------------------------------##
5016#####----------------- T a b l e 203 -----------------#####
5017
5018$check_button_cddb = Gtk2::CheckButton->new ( $langs{name018} );
5019$check_button_cddb->set_active($discDB);
5020$check_button_cddb->signal_connect( "clicked", \&cddb_sensitive );
5021$tooltips->set_tip( $check_button_cddb, $langs{tip209} ) if $show_all_tooltips;
5022$table203->attach($check_button_cddb,  0, 1, 0, 1, 'fill', 'shrink', 0, 0);
5023$check_button_cddb->show;
5024
5025$check_button_overwrite_cddb = Gtk2::CheckButton->new ( $langs{name019} );
5026$check_button_overwrite_cddb->set_active($overwrite_cddb);
5027$tooltips->set_tip( $check_button_overwrite_cddb, $langs{tip210} ) if $show_all_tooltips;
5028$table203->attach($check_button_overwrite_cddb,  1, 2, 0, 1, 'fill', 'shrink', 0, 0);
5029$check_button_overwrite_cddb->show;
5030
5031$label = new Gtk2::Label( $langs{name020} . ":" );
5032$label->set_alignment( 1.0, 0.5 );
5033$table203->attach ($label, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
5034$label->show();
5035
5036$entry_CDDB_server = new Gtk2::Entry( );
5037$entry_CDDB_server->set_text( $CDDB_server );
5038$tooltips->set_tip( $entry_CDDB_server, $langs{tip211} ) if $show_all_tooltips;
5039$table203->attach($entry_CDDB_server,  1, 2, 1, 2, ['fill','expand'], 'fill', 0, 0);
5040$entry_CDDB_server->show;
5041
5042$label = new Gtk2::Label( $langs{name021} . ":" );
5043$label->set_alignment( 1.0, 0.5 );
5044$table203->attach ($label, 2, 3, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5045$label->show();
5046
5047$adj = new Gtk2::Adjustment( $cddb_port, 0, 9999, 1, 0, 0 );
5048$spinner_cddb_port = new Gtk2::SpinButton( $adj, 0.9, 0 );
5049$spinner_cddb_port->set_editable($true);
5050$spinner_cddb_port->set_size_request( 72, -1 );
5051$spinner_cddb_port->set_update_policy( 'if_valid' );
5052$spinner_cddb_port->set_wrap( $false );
5053$tooltips->set_tip( $spinner_cddb_port, $langs{tip212} ) if $show_all_tooltips;
5054$table203->attach($spinner_cddb_port,  3, 4, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5055$spinner_cddb_port->show();
5056
5057$label = new Gtk2::Label( $langs{name022} . ":" );
5058$label->set_alignment( 1.0, 0.5 );
5059$table203->attach ($label, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
5060$label->show();
5061
5062#####--------------------Combo---------------------#####
5063
5064my @transport = ( "cddb", "http" );
5065
5066my $ComboBox_cddb_transport = Gtk2::ComboBox->new_text;
5067foreach (@transport) { $ComboBox_cddb_transport->append_text ($_); }
5068$pos = 0;
5069foreach my $key (@transport) { if ($key eq $cddb_transport) {$ComboBox_cddb_transport->set_active($pos); last;} $pos++; }
5070$ComboBox_cddb_transport->signal_connect("changed", \&ComboBox_choice, \$cddb_transport);
5071$table203->attach ($ComboBox_cddb_transport, 1, 2, 2, 3, 'fill', 'shrink', 0, 0);
5072$ComboBox_cddb_transport->show;
5073
5074#####-------------------final of Combo-------------#####
5075
5076sub cddb_sensitive{
5077   if ($check_button_cddb->get_active ){
5078      $check_button_overwrite_cddb->set_sensitive ($true);
5079      $entry_CDDB_server->set_sensitive ($true);
5080      $spinner_cddb_port->set_sensitive ($true);
5081      $ComboBox_cddb_transport->set_sensitive ($true);
5082   }
5083   else{
5084      $check_button_overwrite_cddb->set_sensitive ($false);
5085      $entry_CDDB_server->set_sensitive ($false);
5086      $spinner_cddb_port->set_sensitive ($false);
5087      $ComboBox_cddb_transport->set_sensitive ($false);
5088   }
5089}
5090cddb_sensitive();
5091
5092##-------------------------------------------------------##
5093#####----------------- T a b l e 204 -----------------#####
5094
5095$button_noia1 = Gtk2::CheckButton->new ( $langs{name023} );
5096$button_noia1->set_active($disable_paranoia);
5097$tooltips->set_tip( $button_noia1, $langs{tip213} ) if $show_all_tooltips;
5098$table204->attach($button_noia1,  0, 1, 0, 1, ['fill','expand'], 'shrink', 0, 0);
5099$button_noia1->show;
5100
5101$button_noia2 = Gtk2::CheckButton->new ( $langs{name026} );
5102$button_noia2->set_active($never_skip_repair);
5103$tooltips->set_tip( $button_noia2, $langs{tip214} ) if $show_all_tooltips;
5104$table204->attach($button_noia2,  1, 2, 0, 1, ['fill','expand'], 'shrink', 0, 0);
5105$button_noia2->show;
5106
5107$button_noia3 = Gtk2::CheckButton->new ( $langs{name024} );
5108$button_noia3->set_active($disable_extra_paranoia);
5109$tooltips->set_tip( $button_noia3, $langs{tip215} ) if $show_all_tooltips;
5110$table204->attach($button_noia3,  0, 1, 1, 2, ['fill','expand'], 'shrink', 0, 0);
5111$button_noia3->show;
5112
5113$button_noia4 = Gtk2::CheckButton->new ( $langs{name025} );
5114$button_noia4->set_active($abort_on_skip);
5115$tooltips->set_tip( $button_noia4, $langs{tip216} ) if $show_all_tooltips;
5116$table204->attach($button_noia4,  0, 1, 2, 3, ['fill','expand'], 'shrink', 0, 0);
5117$button_noia4->show;
5118
5119##-------------------------------------------------------##
5120#####----------------- T a b l e 205 -----------------#####
5121
5122$button_cdda1 = Gtk2::CheckButton->new ("paranoia");
5123$button_cdda1->set_active($paranoia);
5124$tooltips->set_tip( $button_cdda1, $langs{tip217} ) if $show_all_tooltips;
5125$table205->attach($button_cdda1,  0, 1, 0, 1, ['fill','expand'], 'shrink', 0, 0);
5126$button_cdda1->show;
5127
5128$button_cdda2 = Gtk2::CheckButton->new ( $langs{name028} );
5129$button_cdda2->set_active($paranoia_disable);
5130$tooltips->set_tip( $button_cdda2, $langs{tip218} ) if $show_all_tooltips;
5131$table205->attach($button_cdda2,  1, 2, 0, 1, ['fill','expand'], 'shrink', 0, 0);
5132$button_cdda2->show;
5133
5134$button_cdda3 = Gtk2::CheckButton->new ( $langs{name027} );
5135$button_cdda3->set_active($paranoia_no_verify);
5136$tooltips->set_tip( $button_cdda3, $langs{tip219} ) if $show_all_tooltips;
5137$table205->attach($button_cdda3,  0, 1, 1, 2, ['fill','expand'], 'shrink', 0, 0);
5138$button_cdda3->show;
5139
5140
5141###-----------------------------------------------------###
5142#####----------------T a b l e 21 --------------------#####
5143
5144# criar um label (etiqueta - r�tulo) ao lado do menu
5145$label = new Gtk2::Label( $langs{name030} . ":" );
5146$label->set_alignment( 1.0, 0.5 );
5147$table21->attach ($label, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
5148$label->show();
5149
5150#####--------------------Combo---------------------#####
5151
5152my @encode_type_mp3 = ( "average", "constant", "variable" );
5153my @encode_type_mp4 = ( "average", "variable" );
5154my @encode_type_ogg = ( "average", "variable" );
5155my @encode_type_mpc = ( "variable" );
5156# The hash %encode_type save the last encode type
5157
5158my $model_encode = Gtk2::ListStore->new ('Glib::String');
5159foreach (@encode_type_mp3) { $model_encode->set ($model_encode->append, 0, $_); } # fill model
5160
5161
5162$ComboBox_encode_type = Gtk2::ComboBox->new ($model_encode);
5163
5164my $renderer = Gtk2::CellRendererPixbuf->new;
5165$ComboBox_encode_type->pack_start ($renderer, $false);
5166$ComboBox_encode_type->add_attribute ($renderer, stock_id => 0);
5167$renderer = Gtk2::CellRendererText->new;
5168$ComboBox_encode_type->pack_start ($renderer, $true);
5169$ComboBox_encode_type->add_attribute ($renderer, text => 0);
5170
5171#ComboBox_fill_with_array(\$ComboBox_encode_type,\@encode_type_mp3);
5172ComboBox_set_popdown_strings($ComboBox_encode_type,@encode_type_mp3);
5173ComboBox_select_this_text(\$ComboBox_encode_type,\@encode_type_mp3,\$encode_type{'mp3'});
5174
5175$table21->attach($ComboBox_encode_type,  1, 2, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5176$ComboBox_encode_type->signal_connect("changed", \&encode_choice,$ComboBox_encode_type);
5177$ComboBox_encode_type->set_sensitive($change_properties);
5178$ComboBox_encode_type->show;
5179
5180sub ComboBox_set_popdown_strings {
5181   my ($ComboBox_copy, @array) = @_;
5182   return if ($ComboBox_copy->get_active < 0); # return if empty
5183
5184   $ComboBox_copy->get_model->clear;                                        # $model->clear ;; remove all old values
5185   foreach (@array) { $model_encode->set ($model_encode->append, 0, $_); }  # fill the model with new values
5186}
5187
5188## encode type: constant, average or variable bitrate ;; show or hide some widgets
5189sub encode_choice {
5190   my ($self, $ComboBox_copy) = @_;
5191   return if ($ComboBox_copy->get_active < 0); # return if empty
5192   $encode = $ComboBox_copy->get_active_text;
5193   #print "\n---> \$encode = $encode\n";
5194
5195   encode_choice_sensitive();
5196   $status_bar->push($context_id, $langs{msg065}."$extension_output with $encode bitrate." );
5197
5198   # set the last encode type
5199   if ($button_output_mp3->get_active ){$encode_type{'mp3'} = $encode;}
5200   if ($button_output_mp4->get_active ){$encode_type{'mp4'} = $encode;}
5201   if ($button_output_ogg->get_active ){$encode_type{'ogg'} = $encode;}
5202   if ($button_output_mpc->get_active ){$encode_type{'mpc'} = $encode;}
5203
5204   #foreach my $k (sort keys %encode_type){ print "$k => $encode_type{$k}\n"; }
5205}
5206
5207# Set widget sensitive according to encoder choice
5208sub encode_choice_sensitive {
5209   #$encode = $ComboBox_encode_type->get_active_text;   # refresh $encode value
5210
5211   $vb_Max = $ComboBox_Max->get_active_text;; #refresh value
5212   $vb_Min = $ComboBox_Min->get_active_text;
5213
5214   if ($button_output_mp3->get_active ){
5215       if ( $check_change_properties->get_active){
5216           if ( $encode eq "constant" ){ encode_sensitive(1,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1);}
5217	   if ( $encode eq "average"  ){ encode_sensitive(1,0,1,0,0,0,0,1,1,0,0,0,0,1,1,1,1,2,1,1);}
5218	   if ( $encode eq "variable" ){ encode_sensitive(1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,1);}
5219       }
5220       else{
5221           if ( $encode eq "constant" ){ encode_sensitive(0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,1,1);}
5222	   if ( $encode eq "average"  ){ encode_sensitive(0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,2,1,1);}
5223	   if ( $encode eq "variable" ){ encode_sensitive(0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,0,1,1);}
5224       }
5225   }
5226   if ($button_output_mp4->get_active ){
5227       if ( $check_change_properties->get_active){
5228           if ( $encode eq "average" ) { encode_sensitive(1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,2,3,1,2);}
5229	   if ( $encode eq "variable" ){ encode_sensitive(1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,2,3,1,2);}
5230       }
5231       else{ encode_sensitive(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,1,2); }
5232   }
5233   if ($button_output_ogg->get_active ){
5234       if ( $check_change_properties->get_active){
5235           if ( $encode eq "average" ) { encode_sensitive(1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,1,3,4,1,4);}
5236	   if ( $encode eq "variable" ){ encode_sensitive(1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,3,4,1,4);}
5237       }
5238       else{ encode_sensitive(0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,3,4,1,4); }
5239   }
5240   if ($button_output_mpc->get_active ){
5241       if ( $check_change_properties->get_active){
5242             encode_sensitive(1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,4,0,0,3); }
5243       else{ encode_sensitive(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,3); }
5244   }
5245   if ($button_output_ape->get_active ){
5246       if ( $check_change_properties->get_active){
5247             encode_sensitive(0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,5,2,5); }
5248       else{ encode_sensitive(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,2,5); }
5249   }
5250   if ($button_output_flac->get_active ){
5251       if ( $check_change_properties->get_active){
5252             encode_sensitive(0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,6,2,6); }
5253       else{ encode_sensitive(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,6,2,6); }
5254   }
5255   if ($button_output_wav->get_active ){
5256       encode_sensitive(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,1,7);
5257   }
5258
5259   if ($button_output_mp3->get_active){
5260      if ( not $check_change_properties->get_active){
5261         if ( $bitrate_average ){ $status_bar->push($context_id, $langs{msg065}."$extension_output with average bitrate.");}
5262         else { $status_bar->push($context_id, $langs{msg065}."$extension_output with constant bitrate.");}
5263      }
5264      else { $status_bar->push($context_id, $langs{msg065}."$extension_output with $encode bitrate.");}
5265   }
5266   if ($button_output_mp4->get_active){
5267      if ( not $check_change_properties->get_active){
5268         { $status_bar->push($context_id, $langs{msg065}."$extension_output with average bitrate.");}
5269      }
5270      else { $status_bar->push($context_id, $langs{msg065}."$extension_output with $encode bitrate.");}
5271   }
5272   if ($button_output_ogg->get_active){
5273      if ( not $check_change_properties->get_active){
5274         { $status_bar->push($context_id, $langs{msg065}."$extension_output with average bitrate.");}
5275      }
5276      else { $status_bar->push($context_id, $langs{msg065}."$extension_output with $encode bitrate.");}
5277   }
5278   if ($button_output_mpc->get_active){
5279      $status_bar->push($context_id, $langs{msg065}."$extension_output with $encode bitrate.");
5280   }
5281   if ($button_output_ape->get_active){
5282         my $comp_ape = $spinner_compress->get_value_as_int();
5283         $status_bar->push($context_id, $langs{msg065}."$extension_output with compression level = $comp_ape.");
5284   }
5285   if ($button_output_flac->get_active){
5286         my $comp_flac = $spinner_compress_flac->get_value();
5287         $status_bar->push($context_id, $langs{msg065}."$extension_output with compression level = $comp_flac.");
5288   }
5289   if ($button_output_wav->get_active){
5290         $status_bar->push($context_id, $langs{msg065}."$extension_output without compression.");
5291   }
5292
5293}
5294
5295sub encode_sensitive { # there are 20 widget - the order is important
5296   my $opt1 = shift; my $opt2 = shift; my $opt3 = shift; my $opt4 = shift;
5297   my $opt5 = shift; my $opt6 = shift; my $opt7 = shift; my $opt8 = shift;
5298   my $opt9 = shift; my $opt10 = shift; my $opt11 = shift; my $opt12 = shift;
5299   my $opt13 = shift; my $opt14 = shift; my $opt15 = shift; my $opt16 = shift;
5300   my $opt17 = shift; my $opt18 = shift; my $opt19 = shift; my $opt20 = shift;
5301   $ComboBox_encode_type->set_sensitive($opt1);
5302   $ComboBox_const_bitrate->set_sensitive($opt2);
5303   $spinner_bitrate_mp3->set_sensitive($opt3);
5304   $spinner_bitrate_mp4->set_sensitive($opt4);
5305   $spinner_bitrate_ogg->set_sensitive($opt5);
5306   $spinner_compress->set_sensitive($opt6);
5307   $spinner_compress_flac->set_sensitive($opt7);
5308   $ComboBox_Min->set_sensitive($opt8);
5309   $ComboBox_mode->set_sensitive($opt9);
5310   $spinner_V->set_sensitive($opt10);
5311   $spinner_Vmp4->set_sensitive($opt11);
5312   $spinner_Vogg->set_sensitive($opt12);
5313   $spinner_Vmpc->set_sensitive($opt13);
5314   $ComboBox_Max->set_sensitive($opt14);
5315   $spinner_q->set_sensitive($opt15);
5316   $ComboBox_freq->set_sensitive($opt16);
5317   if ($opt17 == 1){
5318      $spinner_V->show;
5319      $spinner_Vmp4->hide;
5320      $spinner_Vogg->hide;
5321      $spinner_Vmpc->hide;
5322   }
5323   if ($opt17 == 2){
5324      $spinner_V->hide;
5325      $spinner_Vmp4->show;
5326      $spinner_Vogg->hide;
5327      $spinner_Vmpc->hide;
5328   }
5329   if ($opt17 == 3){
5330      $spinner_V->hide;
5331      $spinner_Vmp4->hide;
5332      $spinner_Vogg->show;
5333      $spinner_Vmpc->hide;
5334   }
5335   if ($opt17 == 4){
5336      $spinner_V->hide;
5337      $spinner_Vmp4->hide;
5338      $spinner_Vogg->hide;
5339      $spinner_Vmpc->show;
5340   }
5341   if ($opt18 == 1){
5342      $ComboBox_const_bitrate->show;
5343      $spinner_bitrate_mp3->hide;
5344      $spinner_bitrate_mp4->hide;
5345      $spinner_bitrate_ogg->hide;
5346      $spinner_compress->hide;
5347      $spinner_compress_flac->hide;
5348   }
5349   if ($opt18 == 2){
5350      $ComboBox_const_bitrate->hide;
5351      $spinner_bitrate_mp3->show;
5352      $spinner_bitrate_mp4->hide;
5353      $spinner_bitrate_ogg->hide;
5354      $spinner_compress->hide;
5355      $spinner_compress_flac->hide;
5356   }
5357   if ($opt18 == 3){  # mp4
5358      $ComboBox_const_bitrate->hide;
5359      $spinner_bitrate_mp3->hide;
5360      $spinner_bitrate_mp4->show;
5361      $spinner_bitrate_ogg->hide;
5362      $spinner_compress->hide;
5363      $spinner_compress_flac->hide;
5364   }
5365   if ($opt18 == 4){  # ogg
5366      $ComboBox_const_bitrate->hide;
5367      $spinner_bitrate_mp3->hide;
5368      $spinner_bitrate_mp4->hide;
5369      $spinner_bitrate_ogg->show;
5370      $spinner_compress->hide;
5371      $spinner_compress_flac->hide;
5372   }
5373   if ($opt18 == 5){
5374      $ComboBox_const_bitrate->hide;
5375      $spinner_bitrate_mp3->hide;
5376      $spinner_bitrate_mp4->hide;
5377      $spinner_bitrate_ogg->hide;
5378      $spinner_compress->show;
5379      $spinner_compress_flac->hide;
5380   }
5381   if ($opt18 == 6){
5382      $ComboBox_const_bitrate->hide;
5383      $spinner_bitrate_mp3->hide;
5384      $spinner_bitrate_mp4->hide;
5385      $spinner_bitrate_ogg->hide;
5386      $spinner_compress->hide;
5387      $spinner_compress_flac->show;
5388   }
5389   if ($opt19 == 1){
5390      $label_mp3b->show;
5391      $label_comp->hide;
5392   }
5393   if ($opt19 == 2){
5394      $label_mp3b->hide;
5395      $label_comp->show;
5396   }
5397   if ($opt20 == 1){
5398      $entry_command_mp3->show;
5399      $entry_command_mp4->hide;
5400      $entry_command_mpc->hide;
5401      $entry_command_ogg->hide;
5402      $entry_command_ape->hide;
5403      $entry_command_flac->hide;
5404   }
5405   if ($opt20 == 2){ # mp4
5406      $entry_command_mp3->hide;
5407      $entry_command_mp4->show;
5408      $entry_command_mpc->hide;
5409      $entry_command_ogg->hide;
5410      $entry_command_ape->hide;
5411      $entry_command_flac->hide;
5412   }
5413   if ($opt20 == 3){ # mpc
5414      $entry_command_mp3->hide;
5415      $entry_command_mp4->hide;
5416      $entry_command_mpc->show;
5417      $entry_command_ogg->hide;
5418      $entry_command_ape->hide;
5419      $entry_command_flac->hide;
5420   }
5421   if ($opt20 == 4){  # ogg
5422      $entry_command_mp3->hide;
5423      $entry_command_mp4->hide;
5424      $entry_command_mpc->hide;
5425      $entry_command_ogg->show;
5426      $entry_command_ape->hide;
5427      $entry_command_flac->hide;
5428   }
5429   if ($opt20 == 5){
5430      $entry_command_mp3->hide;
5431      $entry_command_mp4->hide;
5432      $entry_command_mpc->hide;
5433      $entry_command_ogg->hide;
5434      $entry_command_ape->show;
5435      $entry_command_flac->hide;
5436   }
5437   if ($opt20 == 6){ #flac
5438      $entry_command_mp3->hide;
5439      $entry_command_mp4->hide;
5440      $entry_command_mpc->hide;
5441      $entry_command_ogg->hide;
5442      $entry_command_ape->hide;
5443      $entry_command_flac->show;
5444   }
5445   if ($opt20 == 7){ #wav
5446      $entry_command_mp3->hide;
5447      $entry_command_mp4->hide;
5448      $entry_command_mpc->hide;
5449      $entry_command_ogg->hide;
5450      $entry_command_ape->hide;
5451      $entry_command_flac->hide;
5452   }
5453}
5454
5455#####-------------------final of Combo-------------#####
5456
5457# criar um label (etiqueta - r�tulo) ao lado do menu
5458$label_mp3b = new Gtk2::Label( "Bitrate:" );
5459$label_mp3b->set_alignment( 1, 0.5 );
5460$table21->attach ($label_mp3b, 4, 5, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5461$label_mp3b->show();
5462
5463$label_comp = new Gtk2::Label( "Comp:" );
5464$label_comp->set_alignment( 1, 0.5 );
5465$table21->attach ($label_comp, 4, 5, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5466#$label_comp->show();
5467
5468#####--------------------ComboBox------------------#####
5469my @all_const_bitrate = ( "32", "40", "48", "56", "64", "80", "96", "112", "128", "160",  "192", "224", "256", "320" );
5470
5471$ComboBox_const_bitrate = Gtk2::ComboBox->new_text;
5472ComboBox_fill_with_array(\$ComboBox_const_bitrate,\@all_const_bitrate);
5473ComboBox_select_this_text(\$ComboBox_const_bitrate,\@all_const_bitrate,\$bitrate_constant);  #standard value
5474$ComboBox_const_bitrate->set_size_request( 72, -1 );
5475$ComboBox_const_bitrate->signal_connect("changed", \&BitrateChanged);
5476$table21->attach ($ComboBox_const_bitrate, 5, 6, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5477#$ComboBox_const_bitrate->show;
5478
5479## audio mp3 bitrate is changed
5480sub BitrateChanged {
5481   $bitrate_constant = $ComboBox_const_bitrate->get_active_text; # copy the value of bitrate changed
5482   $status_bar->push($context_id, " constant $extension_output bitrate set to $bitrate_constant Kb/s");
5483   #print "\$bitrate_constant = $bitrate_constant \n";
5484}
5485
5486#####----------------final of ComboBox-------------#####
5487
5488###--------------------SpinButton--------------------###
5489
5490# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
5491$adj = new Gtk2::Adjustment( $bitrate_avr_mp3, 8, 320, 1, 5, 0 );
5492#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
5493$spinner_bitrate_mp3 = new Gtk2::SpinButton( $adj, 0.5, 0 );
5494$spinner_bitrate_mp3->set_wrap( $true );
5495$spinner_bitrate_mp3->set_size_request( 72, -1 );
5496$spinner_bitrate_mp3->set_editable($false);
5497$spinner_bitrate_mp3->set_update_policy( 'if_valid' );
5498$tooltips->set_tip( $spinner_bitrate_mp3, $langs{tip220} ) if $show_all_tooltips;
5499$table21->attach($spinner_bitrate_mp3,  5, 6, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5500$spinner_bitrate_mp3->signal_connect("value-changed", sub{ $bitrate_avr_mp3 = $spinner_bitrate_mp3->get_value_as_int(); } );
5501#$spinner_bitrate_mp3->set_sensitive($false);
5502$spinner_bitrate_mp3->show();
5503
5504$adj = new Gtk2::Adjustment( $bitrate_avr_ogg, 8, 320, 1, 5, 0 );
5505$spinner_bitrate_ogg = new Gtk2::SpinButton( $adj, 0.5, 0 );
5506$spinner_bitrate_ogg->set_wrap( $true );
5507$spinner_bitrate_ogg->set_size_request( 72, -1 );
5508$spinner_bitrate_ogg->set_editable($false);
5509$spinner_bitrate_ogg->set_update_policy( 'if_valid' );
5510$tooltips->set_tip( $spinner_bitrate_ogg, $langs{tip220} ) if $show_all_tooltips;
5511$table21->attach($spinner_bitrate_ogg,  5, 6, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5512$spinner_bitrate_ogg->signal_connect("value-changed", sub{ $bitrate_avr_ogg = $spinner_bitrate_ogg->get_value_as_int(); } );
5513#$spinner_bitrate_mp3->set_sensitive($false);
5514#$spinner_bitrate_mp3->show();
5515
5516$adj = new Gtk2::Adjustment( $bitrate_avr_mp4, 8, 320, 1, 5, 0 );
5517$spinner_bitrate_mp4 = new Gtk2::SpinButton( $adj, 0.5, 0 );
5518$spinner_bitrate_mp4->set_wrap( $true );
5519$spinner_bitrate_mp4->set_size_request( 72, -1 );
5520$spinner_bitrate_mp4->set_editable($false);
5521$spinner_bitrate_mp4->set_update_policy( 'if_valid' );
5522$tooltips->set_tip( $spinner_bitrate_mp4, $langs{tip220} ) if $show_all_tooltips;
5523$table21->attach($spinner_bitrate_mp4,  5, 6, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5524$spinner_bitrate_mp4->signal_connect("value-changed", sub{ $bitrate_avr_mp4 = $spinner_bitrate_mp4->get_value_as_int(); } );
5525#$spinner_bitrate_mp3->set_sensitive($false);
5526#$spinner_bitrate_mp3->show();
5527
5528# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
5529$adj = new Gtk2::Adjustment( $comp_ape, 1000, 5000, 1000, 0, 0 );
5530#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
5531$spinner_compress = new Gtk2::SpinButton( $adj, 0.1, 0 );
5532$spinner_compress->set_wrap( $true );
5533$spinner_compress->set_size_request( 72, -1 );
5534$spinner_compress->set_editable($false);
5535$spinner_compress->set_update_policy( 'if_valid' );
5536$spinner_compress->signal_connect("value-changed",
5537sub{
5538   my $comp_ape = $spinner_compress->get_value_as_int();
5539   $status_bar->push($context_id, $langs{msg065}."$extension_output with compression level = $comp_ape.");
5540} );
5541$tooltips->set_tip( $spinner_compress, $langs{tip226} ) if $show_all_tooltips;
5542$table21->attach($spinner_compress,  5, 6, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5543#$spinner_compress->show();
5544# $comp = $spinner_compress->get_value_as_int();
5545
5546# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
5547$adj = new Gtk2::Adjustment( $comp_flac, 0, 8, 1, 0, 0 );
5548#$spin = new Gtk::SpinButton( $adjustment, $climb_rate, $digits );
5549$spinner_compress_flac = new Gtk2::SpinButton( $adj, 0.1, 0 );
5550$spinner_compress_flac->set_wrap( $true );
5551$spinner_compress_flac->set_size_request( 72, -1 );
5552$spinner_compress_flac->set_editable($false);
5553$spinner_compress_flac->set_update_policy( 'if_valid' );
5554$spinner_compress_flac->signal_connect("value-changed",
5555sub{
5556   my $comp_flac = $spinner_compress_flac->get_value_as_int();
5557   $status_bar->push($context_id, $langs{msg065}."$extension_output with compression level = $comp_flac.");
5558} );
5559$tooltips->set_tip( $spinner_compress_flac, $langs{tip227} ) if $show_all_tooltips;
5560$table21->attach($spinner_compress_flac,  5, 6, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5561#$spinner_compress->show();
5562# $comp = $spinner_compress->get_value_as_int();
5563
5564###-------------final-of--SpinButton-----------------###
5565
5566# criar um label (etiqueta - r�tulo) ao lado do menu
5567$label = new Gtk2::Label( "Kb/s" );
5568$label->set_alignment( 0.0, 0.5 );
5569$table21->attach ($label, 6, 7, 0, 1, 'fill', 'shrink', 0, 0);
5570$label->show();
5571
5572#####--------------------Combo---------------------#####
5573# Min BitRate Quality
5574
5575# criar um label (etiqueta - r�tulo) ao lado do menu
5576$label = new Gtk2::Label( "Min:" );
5577$label->set_alignment( 1, 0.5 );
5578$table21->attach ($label, 7, 8, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5579$label->show;
5580
5581$ComboBox_Min = Gtk2::ComboBox->new_text;
5582ComboBox_fill_with_array(\$ComboBox_Min,\@all_const_bitrate);
5583ComboBox_select_this_text(\$ComboBox_Min,\@all_const_bitrate,\$vb_Min);  #standard value
5584$ComboBox_Min->set_size_request( 72, -1 );
5585$ComboBox_Min->signal_connect("changed", \&bit_Min);
5586$table21->attach ($ComboBox_Min, 8, 9, 0, 1, ['expand','fill'], 'shrink', 0, 0);
5587$ComboBox_Min->set_sensitive($true);
5588$ComboBox_Min->show;
5589
5590## Minimum Bitrate with variable encode
5591sub bit_Min {
5592   $vb_Min = $ComboBox_Min->get_active_text;
5593
5594   if ( $vb_Min > $vb_Max ) { # always assert this condition
5595      $vb_Min = $vb_Max;
5596      ComboBox_select_this_text(\$ComboBox_Min,\@all_const_bitrate,\$vb_Min);
5597   }
5598}
5599#####-------------------final of Combo-------------#####
5600
5601
5602#####--------------------Combo---------------------#####
5603# Mode for mp3 files
5604
5605# criar um label (etiqueta - r�tulo) ao lado do menu
5606my $label_combo_mode = new Gtk2::Label( $langs{name031} . ":" );
5607$label_combo_mode->set_alignment( 1, 0.5 );
5608$table21->attach ($label_combo_mode, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
5609$label_combo_mode->show();
5610
5611my @all_mode = ( 'joint stereo', 'stereo', 'forced joint stereo', 'dual channels', 'mono' );
5612
5613$ComboBox_mode = Gtk2::ComboBox->new_text;
5614ComboBox_fill_with_array(\$ComboBox_mode,\@all_mode);
5615ComboBox_select_this_text(\$ComboBox_mode,\@all_mode,\$mode);  #standard value
5616$ComboBox_mode->set_size_request( 104, -1 );
5617$table21->attach ($ComboBox_mode, 1, 2, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5618$ComboBox_mode->show;
5619
5620#####-------------------final of Combo-------------#####
5621#####--------------------Combo---------------------#####
5622# Variable  BitRate Quality
5623
5624# criar um label (etiqueta - r�tulo) ao lado do menu
5625$label_VB = new Gtk2::Label( $langs{name033} . ":" );
5626$label_VB->set_alignment( 1, 0.5 );
5627$table21->attach ($label_VB, 4, 5, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5628$label_VB->show();
5629
5630###--------------------SpinButton--------------------###
5631
5632$adj = new Gtk2::Adjustment( $V, 0, 9, 1, 0, 0 );
5633$spinner_V = new Gtk2::SpinButton( $adj, 0.1, 0 );
5634$spinner_V->set_editable($false);
5635$spinner_V->set_size_request( 72, -1 );
5636$spinner_V->set_update_policy( 'if_valid' );
5637$spinner_V->set_wrap( $false );
5638$tooltips->set_tip( $spinner_V, $langs{tip221} ) if $show_all_tooltips;
5639$table21->attach($spinner_V,  5, 6, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5640$spinner_V->set_sensitive($false);
5641$spinner_V->show();
5642
5643# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
5644$adj = new Gtk2::Adjustment( $Vogg, -1.0, 10.0, 0.1, 0, 0 );
5645$spinner_Vogg = new Gtk2::SpinButton($adj, 0.4, 1);
5646$spinner_Vogg->set_editable($false);
5647$spinner_Vogg->set_size_request( 72, -1 );
5648$spinner_Vogg->set_update_policy( 'if_valid' );
5649$spinner_Vogg->set_wrap( $false );
5650$tooltips->set_tip( $spinner_Vogg, $langs{tip222} ) if $show_all_tooltips;
5651$table21->attach($spinner_Vogg,  5, 6, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5652#$spinner_Vogg->set_sensitive($false);
5653#$spinner_Vogg->show();
5654
5655# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
5656$adj = new Gtk2::Adjustment( $Vmpc, 2, 8, 1, 0, 0 );
5657$spinner_Vmpc = new Gtk2::SpinButton($adj, 0.4, 0);
5658$spinner_Vmpc->set_editable($false);
5659$spinner_Vmpc->set_size_request( 72, -1 );
5660$spinner_Vmpc->set_update_policy( 'if_valid' );
5661$spinner_Vmpc->set_wrap( $false );
5662$tooltips->set_tip( $spinner_Vmpc, $langs{tip225} ) if $show_all_tooltips;
5663$table21->attach($spinner_Vmpc,  5, 6, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5664#$spinner_Vogg->set_sensitive($false);
5665#$spinner_Vogg->show();
5666
5667# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
5668$adj = new Gtk2::Adjustment( $Vmp4, 10, 500, 1, 0, 0 );
5669$spinner_Vmp4 = new Gtk2::SpinButton($adj, 0.8, 0);
5670$spinner_Vmp4->set_editable($false);
5671$spinner_Vmp4->set_size_request( 72, -1 );
5672$spinner_Vmp4->set_update_policy( 'if_valid' );
5673$spinner_Vmp4->set_wrap( $false );
5674$tooltips->set_tip( $spinner_Vmp4, $langs{tip224} ) if $show_all_tooltips;
5675$table21->attach($spinner_Vmp4,  5, 6, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5676#$spinner_Vogg->set_sensitive($false);
5677#$spinner_Vogg->show();
5678
5679###-------------final-of--SpinButton-----------------###
5680#####--------------------Combo---------------------#####
5681# Max BitRate Quality
5682
5683# criar um label (etiqueta - r�tulo) ao lado do menu
5684$label = new Gtk2::Label( "Max:" );
5685$label->set_alignment( 1, 0.5 );
5686$table21->attach ($label, 7, 8, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5687$label->show;
5688
5689$ComboBox_Max = Gtk2::ComboBox->new_text;
5690ComboBox_fill_with_array(\$ComboBox_Max,\@all_const_bitrate);
5691ComboBox_select_this_text(\$ComboBox_Max,\@all_const_bitrate,\$vb_Max);  #standard value
5692$ComboBox_Max->set_size_request( 72, -1 );
5693$ComboBox_Max->signal_connect("changed", \&bit_Max);
5694$table21->attach ($ComboBox_Max, 8, 9, 1, 2, ['expand','fill'], 'shrink', 0, 0);
5695$ComboBox_Max->set_sensitive($true);
5696$ComboBox_Max->show;
5697
5698sub bit_Max {
5699   $vb_Max = $ComboBox_Max->get_active_text;
5700
5701   if ( $vb_Max < $vb_Min ) { # always assert this condition
5702      $vb_Max = $vb_Min;
5703      ComboBox_select_this_text(\$ComboBox_Max,\@all_const_bitrate,\$vb_Max);
5704   }
5705}
5706
5707#####-------------------final of Combo-------------#####
5708
5709#####-------------------final of Combo-------------#####
5710
5711# criar um label (etiqueta - r�tulo) ao lado do menu
5712$label = new Gtk2::Label( $langs{name032} . ":" );
5713$label->set_alignment( 1.0, 0.5 );
5714$table21->attach ($label, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
5715$label->show();
5716
5717###--------------------SpinButton--------------------###
5718
5719# $adj = new Gtk::Adjustment( $value,$lower,$upper,$step_increment,$page_increment,$page_size );
5720$adj = new Gtk2::Adjustment( $quality, 0, 9, 1, 5, 0 );
5721#$spin = new Gtk2::SpinButton( $adjustment, $climb_rate, $digits );
5722$spinner_q = new Gtk2::SpinButton( $adj, 0, 0 );
5723$spinner_q->set_wrap( $true );
5724$spinner_q->set_editable($false);
5725$spinner_q->set_size_request( 72, -1 );
5726$spinner_q->set_update_policy( 'if_valid' );
5727$tooltips->set_tip( $spinner_q, $langs{tip223} ) if $show_all_tooltips;
5728$table21->attach($spinner_q,  1, 2, 2, 3, ['expand','fill'], 'shrink', 0, 0);
5729$spinner_q->signal_connect("value-changed", sub{ $quality = $spinner_q->get_value; } );
5730$spinner_q->show();
5731
5732###-------------final-of--SpinButton-----------------###
5733
5734#####--------------------Combo---------------------#####
5735# Frequency
5736
5737# criar um label (etiqueta - r�tulo) ao lado do menu
5738$label = new Gtk2::Label( $langs{name041} . ":" );
5739$label->set_alignment( 1, 0.5 );
5740$table21->attach ($label, 4, 5, 2, 3, ['expand','fill'], 'shrink', 0, 0);
5741$label->show();
5742
5743my @all_freq = ( "8000", "11025", "12000", "16000", "22050", "24000", "32000", "44100", "48000" );
5744
5745$ComboBox_freq = Gtk2::ComboBox->new_text;
5746ComboBox_fill_with_array(\$ComboBox_freq,\@all_freq);
5747ComboBox_select_this_text(\$ComboBox_freq,\@all_freq,\$freq);  #standard value
5748$ComboBox_freq->set_size_request( 72, -1 );
5749#$ComboBox_freq->signal_connect("changed", \&BitrateChanged);
5750$table21->attach ($ComboBox_freq, 5, 6, 2, 3, ['expand','fill'], 'shrink', 0, 0);
5751$ComboBox_freq->set_sensitive($change_properties);
5752$ComboBox_freq->show;
5753
5754$label = new Gtk2::Label( "Hz" );
5755$label->set_alignment( 0.0, 0.5 );
5756$table21->attach ($label, 6, 7, 2, 3, 'fill', 'shrink', 0, 0);
5757$label->show();
5758
5759#####-------------------final of Combo-------------#####
5760
5761$label = new Gtk2::Label( $langs{name013} . ":" );
5762$label->set_alignment( 1.0, 0.5 );
5763$table21->attach ($label, 0, 1, 3, 4, 'fill', 'shrink', 0, 0);
5764$label->show();
5765
5766# Create the Entry to put additional command line
5767$entry_command_mp3 = new Gtk2::Entry( );
5768$entry_command_mp3->set_text( $additional_command_mp3 );
5769$tooltips->set_tip( $entry_command_mp3, $langs{tip228} ) if $show_all_tooltips;
5770$table21->attach($entry_command_mp3,  1, 9, 3, 4, ['fill','expand'], 'fill', 0, 0);
5771
5772$entry_command_mp4 = new Gtk2::Entry( );
5773$entry_command_mp4->set_text( $additional_command_mp4 );
5774$tooltips->set_tip( $entry_command_mp4, $langs{tip228} ) if $show_all_tooltips;
5775$table21->attach($entry_command_mp4,  1, 9, 3, 4, ['fill','expand'], 'fill', 0, 0);
5776
5777$entry_command_mpc = new Gtk2::Entry( );
5778$entry_command_mpc->set_text( $additional_command_mpc );
5779$tooltips->set_tip( $entry_command_mpc, $langs{tip228} ) if $show_all_tooltips;
5780$table21->attach($entry_command_mpc,  1, 9, 3, 4, ['fill','expand'], 'fill', 0, 0);
5781
5782$entry_command_ogg = new Gtk2::Entry( );
5783$entry_command_ogg->set_text( $additional_command_ogg );
5784$tooltips->set_tip( $entry_command_ogg, $langs{tip228} ) if $show_all_tooltips;
5785$table21->attach($entry_command_ogg,  1, 9, 3, 4, ['fill','expand'], 'fill', 0, 0);
5786
5787$entry_command_ape = new Gtk2::Entry( );
5788$entry_command_ape->set_text( $additional_command_ape );
5789$tooltips->set_tip( $entry_command_ape, $langs{tip228} ) if $show_all_tooltips;
5790$table21->attach($entry_command_ape,  1, 9, 3, 4, ['fill','expand'], 'fill', 0, 0);
5791
5792$entry_command_flac = new Gtk2::Entry( );
5793$entry_command_flac->set_text( $additional_command_flac );
5794$tooltips->set_tip( $entry_command_flac, $langs{tip228} ) if $show_all_tooltips;
5795$table21->attach($entry_command_flac,  1, 9, 3, 4, ['fill','expand'], 'fill', 0, 0);
5796
5797
5798###-----------------------------------------------------###
5799#####---------------- T a b l e 3 --------------------#####
5800
5801###----------------id3 tag & mpeg info---------------###
5802
5803$label = new Gtk2::Label( $langs{name034} . ":" ); # table 'info'
5804$label->set_alignment( 1.0, 0.5 );
5805$table3->attach ($label, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
5806$label->show();
5807
5808# Create the Entry to put the title of mp3 music
5809my $entry_title = new Gtk2::Entry();
5810$entry_title->set_text( "" );
5811$entry_title->signal_connect("changed",\&change_tag);
5812$table3->attach_defaults ($entry_title, 1, 6, 0, 1 );
5813$entry_title->show();
5814
5815$label = new Gtk2::Label( $langs{name035} . ":" );
5816$label->set_alignment( 1.0, 0.5 );
5817$table3->attach ($label, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
5818$label->show();
5819
5820# Create the Entry to put the artist of mp3 music
5821my $entry_artist = new Gtk2::Entry();
5822$entry_artist->set_text( "" );
5823$entry_artist->signal_connect("changed",\&change_tag);
5824$tooltips->set_tip( $entry_artist, $langs{tip301}) if $show_all_tooltips;
5825$table3->attach_defaults ($entry_artist, 1, 6, 1, 2 );
5826$entry_artist->show();
5827
5828$label = new Gtk2::Label( $langs{name036} . ":" );
5829$label->set_alignment( 1.0, 0.5 );
5830$table3->attach ($label, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
5831$label->show();
5832
5833# Create the Entry to put the album of mp3 music
5834my $entry_album = new Gtk2::Entry();
5835$entry_album->set_text( "" );
5836$entry_album->signal_connect("changed",\&change_tag);
5837$table3->attach_defaults ($entry_album, 1, 6, 2, 3 );
5838$entry_album->show();
5839
5840$label = new Gtk2::Label( $langs{name037} . ":" );
5841$label->set_alignment( 1.0, 0.5 );
5842$table3->attach ($label, 0, 1, 3, 4, 'fill', 'shrink', 0, 0);
5843$label->show();
5844
5845# Create the Entry to put the comment of mp3 music
5846my $entry_comment = new Gtk2::Entry();
5847$entry_comment->set_text( "" );
5848$entry_comment->signal_connect("changed",\&change_tag);
5849$table3->attach_defaults ($entry_comment, 1, 6, 3, 4 );
5850$entry_comment->show();
5851
5852$label = new Gtk2::Label( $langs{name038} . ":" );
5853$label->set_alignment( 1.0, 0.5 );
5854$table3->attach ($label, 0, 1, 4, 5, 'fill', 'shrink', 0, 0);
5855$label->show();
5856
5857my $entry_year = new Gtk2::Entry();
5858$entry_year->set_text( "" );
5859$entry_year->set_size_request(50);
5860$entry_year->signal_connect("changed",\&change_tag);
5861$table3->attach_defaults ($entry_year, 1, 2, 4, 5 );
5862$entry_year->show();
5863
5864$label = new Gtk2::Label( $langs{name039} . ":" );
5865$label->set_alignment( 1.0, 0.5 );
5866$table3->attach ($label, 2, 3, 4, 5, 'fill', 'shrink', 0, 0);
5867$label->show();
5868
5869# Create the Entry to put the track number of mp3 music
5870my $entry_tn = new Gtk2::Entry();
5871$entry_tn->set_text( "" );
5872$entry_tn->set_size_request(40);
5873$tooltips->set_tip( $entry_tn, $langs{tip302} ) if $show_all_tooltips;
5874$entry_tn->signal_connect("changed",\&change_tag);
5875$table3->attach_defaults ($entry_tn, 3, 4, 4, 5 );
5876$entry_tn->show();
5877
5878$label = new Gtk2::Label( "/" );
5879$label->set_alignment( 1.0, 0.5 );
5880$table3->attach ($label, 4, 5, 4, 5, 'fill', 'shrink', 0, 0);
5881$label->show();
5882
5883my $entry_tt = new Gtk2::Entry();
5884$entry_tt->set_text( "" );
5885$entry_tt->set_size_request(40);
5886$tooltips->set_tip( $entry_tt, $langs{tip303} ) if $show_all_tooltips;
5887$entry_tt->signal_connect("changed",\&change_tag);
5888$table3->attach_defaults ($entry_tt, 5, 6, 4, 5 );
5889$entry_tt->show();
5890
5891#####----------------Combo---mp3 info--------------#####
5892# Genre of mp3 info - see  mp3info  -G
5893
5894# criar um label (etiqueta - r�tulo) ao lado do menu
5895$label = new Gtk2::Label( $langs{name040} . ":" );
5896$label->set_alignment( 1.0, 0.5 );
5897$table3->attach ($label, 0, 1, 5, 6, 'fill', 'shrink', 0, 0);
5898$label->show();
5899
5900# my $cmd = "mp3info -G";
5901# my $cmd = "lame --genre-list"; # see the command: < lame --genre-list >
5902# my $list_genre = exec_cmd_system($cmd); # list with all mp3 genre
5903# $list_genre =~ s/\s*(\d+)\s(.*)/ $1 => "$2", /g;
5904# print "$list_genre";
5905
5906# A hash represents a set of key/value pairs:  --- see perlintro, perlref, perlreftut
5907# 0 - 147 genre are default;; more than 148 is personal ;;  max number = 255
5908my %genre_hash = (  123 => "A Cappella",  34 => "Acid",  74 => "Acid Jazz",  73 => "Acid Punk",  99 => "Acoustic",
590920 => "Alternative",  40 => "Alt. Rock",  26 => "Ambient",  145 => "Anime",  90 => "Avantgarde",  116 => "Ballad",
591041 => "Bass",  135 => "Beat",  85 => "Bebob",  96 => "Big Band",  138 => "Black Metal",  89 => "Bluegrass",  0 => "Blues",
5911107 => "Booty Bass",  132 => "BritPop",  65 => "Cabaret",  88 => "Celtic",  104 => "Chamber Music",  102 => "Chanson",
591297 => "Chorus",  136 => "Christian Gangsta Rap",  61 => "Christian Rap",  141 => "Christian Rock",  32 => "Classical",
59131 => "Classic Rock",  112 => "Club",  128 => "Club-House",  57 => "Comedy",  140 => "Contemporary Christian",  2 => "Country",
5914139 => "Crossover",  58 => "Cult",  3 => "Dance",  125 => "Dance Hall",  50 => "Darkwave",  22 => "Death Metal",  4 => "Disco",
591555 => "Dream",  127 => "Drum & Bass",  122 => "Drum Solo",  120 => "Duet",  98 => "Easy Listening",  52 => "Electronic",
591648 => "Ethnic",  54 => "Eurodance",  124 => "Euro-House",  25 => "Euro-Techno",  84 => "Fast-Fusion",  80 => "Folk",
5917115 => "Folklore",  81 => "Folk/Rock",  119 => "Freestyle",  5 => "Funk",  30 => "Fusion",  36 => "Game",
591859 => "Gangsta Rap",  126 => "Goa",  38 => "Gospel",  49 => "Gothic",  91 => "Gothic Rock",  6 => "Grunge",
5919129 => "Hardcore",  79 => "Hard Rock",  137 => "Heavy Metal",  7 => "Hip-Hop",  35 => "House",  100 => "Humour",
5920131 => "Indie",  19 => "Industrial",  33 => "Instrumental",  46 => "Instrumental Pop",  47 => "Instrumental Rock",
59218 => "Jazz",  29 => "Jazz+Funk",  146 => "JPop",  63 => "Jungle",  86 => "Latin",  71 => "Lo-Fi",  45 => "Meditative",
5922142 => "Merengue",  9 => "Metal",  77 => "Musical",  82 => "National Folk",  64 => "Native American",  133 => "Negerpunk",
592310 => "New Age",  66 => "New Wave",  39 => "Noise",  11 => "Oldies",  103 => "Opera",  12 => "Other",  75 => "Polka",
5924134 => "Polsk Punk",  13 => "Pop",  53 => "Pop-Folk",  62 => "Pop/Funk",  109 => "Porn Groove",  117 => "Power Ballad",
592523 => "Pranks",  108 => "Primus",  92 => "Progressive Rock",  67 => "Psychedelic",  93 => "Psychedelic Rock",  43 => "Punk",
5926121 => "Punk Rock",  15 => "Rap",  68 => "Rave",  14 => "R&B",  16 => "Reggae",  76 => "Retro",  87 => "Revival",
5927118 => "Rhythmic Soul",  17 => "Rock",  78 => "Rock & Roll",  143 => "Salsa",  114 => "Samba",  110 => "Satire",
592869 => "Showtunes",  21 => "Ska",  111 => "Slow Jam",  95 => "Slow Rock",  105 => "Sonata",  42 => "Soul",  37 => "Sound Clip",
592924 => "Soundtrack",  56 => "Southern Rock",  44 => "Space",  101 => "Speech",  83 => "Swing",  94 => "Symphonic Rock",
5930106 => "Symphony",  147 => "Synthpop",  113 => "Tango",  18 => "Techno",  51 => "Techno-Industrial",  130 => "Terror",
5931144 => "Thrash Metal",  60 => "Top 40",  70 => "Trailer",  31 => "Trance",  72 => "Tribal",  27 => "Trip-Hop",  28 => "Vocal",
5932# this is additional genre from Brazilian music
5933148 => "MPB", 149 => "Bossa Nova", 150 => "Capoeira", 151 => "Chorinho", 152 => "Forr�", 153 => "Maracatu",
5934154 => "Timbalada", 155 => "Ax�", 156 => "Sertanejo", 157 => "Pagode",
5935# this is additional genre
5936158 => "World Music"
5937);
5938
5939# print "\nhash of genre --- element 32: ",$genre_hash{"32"}," \n";
5940my $count_pairs = keys %genre_hash; # Get the number of elements (key-value pairs) in the hash.
5941#print "count_pairs = $count_pairs\n";
5942
5943my @array_genre = (); # array with only the names
5944for my $key (0..158) {
5945   #print "$key => $genre_hash{$key}\n";
5946   push @array_genre,$genre_hash{$key};
5947}
5948@array_genre = sort @array_genre;
5949
5950my $ComboBoxEntry_genre = Gtk2::ComboBoxEntry->new_text;
5951ComboBox_fill_with_array(\$ComboBoxEntry_genre,\@array_genre);
5952#ComboBox_select_this_text(\$ComboBoxEntry_genre,\@array_genre,"");  #standard value
5953$ComboBoxEntry_genre->child->set_text( "" );  #standard value
5954$ComboBoxEntry_genre->signal_connect("changed", \&change_tag);
5955$table3->attach ($ComboBoxEntry_genre, 1, 6, 5, 6, 'fill', 'fill', 0, 0);
5956$ComboBoxEntry_genre->show;
5957
5958
5959# when whatever tag is changed, the button_save_tag is set to set_sensitive($true)
5960my $abandon_change_tag = $false; # if '$abandon_change_tag eq $true' then force to abandon the 'sub change_tag'
5961
5962sub change_tag {  # sub cell_edited {
5963
5964   return if $abandon_change_tag;
5965
5966   # get selected row  from 'sub get_selection_tree' that will be used by 'sub change_tag' and 'sub save_tag'
5967   my $row = $files_info[0]{get_selection_row};
5968
5969   return unless defined $row;
5970
5971   my $filepath        = $files_info[$row]{filepath};
5972   #$filepath = filename_from_unicode $filepath if( ! -e $filepath );
5973   my $extension_input = $files_info[$row]{extension_input};
5974
5975   # get all current 8 entries:
5976   my $Title   = $files_info[$row]{title};
5977   my $Artist  = $files_info[$row]{artist};
5978   my $Comment = $files_info[$row]{comment};
5979   my $Album   = $files_info[$row]{album};
5980   my $Genre   = $files_info[$row]{genre};
5981   my $Year    = $files_info[$row]{year};
5982   my $Track_Number = $files_info[$row]{track};
5983   my $Total_Track  = $files_info[$row]{total_track};
5984
5985   #print "\n sub change_tag --->1 \$row = $row \$filepath = $filepath ;; \$Title = $Title ;; \$Album = $Album ;; \$extension_input = $extension_input\n";
5986
5987
5988      my $title  = $entry_title->get_text;
5989      my $artist = $entry_artist->get_text;
5990      my $album  = $entry_album->get_text;
5991      my $comment= $entry_comment->get_text;
5992      my $genre  = $ComboBoxEntry_genre->child->get_text;
5993
5994      # some verifications/validations
5995      my $year = "invalid";
5996      if ( $entry_year->get_text =~ /^(\d{1,4})$/ ) { $year = $1; }
5997      if ( $entry_year->get_text eq "" ) { $year = ""; }
5998      my $tn = "invalid"; # track number
5999      if ( $entry_tn->get_text =~ /^(\d+)$/ ) { $tn = $1; }
6000      if ( $entry_tn->get_text eq "" ) { $tn = ""; }
6001      my $tt = "invalid"; # track total
6002      if ( $entry_tt->get_text =~ /^(\d+)$/ ) { $tt = $1; }
6003      if ( $entry_tt->get_text eq "" ) { $tt = ""; }
6004
6005      # verify any change in tag: Title, Artist, ...
6006      if ( ($extension_input =~ /(mp3|mpc|ogg|ape|flac|wav)/) and ($title ne $Title or $artist ne $Artist or $album ne $Album
6007          or $comment ne $Comment or $year ne $Year or $tn ne $Track_Number or $tt ne $Total_Track or $genre ne $Genre) )
6008      {
6009          # Test if the $filepath is writable. '-w'  File is writable by effective uid/gid.
6010          if ( not file_is_writable($filepath) ) {
6011	      $button_save_tag->set_sensitive($false);
6012	      return $false;
6013	  }
6014          if ( $tn =~ /^(\d+)$/ and $tt =~ /^(\d+)$/ ) {
6015	     if ( $tt < $tn ){ # only compare for numbers
6016	        $status_bar->push($context_id, $langs{msg080} );
6017	        $button_save_tag->set_sensitive($false);
6018	        return $false;
6019	     }
6020	  }
6021	  if ( ( $tn eq "" and $tt =~ /^(\d+)$/ ) or $tn eq "invalid" or $tt eq "invalid" or $year eq "invalid" ) {
6022	     $status_bar->push($context_id, $langs{msg080} );
6023	     $button_save_tag->set_sensitive($false);
6024	     return $false;
6025	  }
6026          $tooltips->set_tip( $button_save_tag, $langs{tip304} ) if $show_all_tooltips;
6027      }
6028      elsif ( ($extension_input =~ /(cda)/) and ($title ne $Title or $artist ne $Artist or $album ne $Album
6029          or $year ne $Year or $genre ne $Genre) )
6030      {
6031          if ( $year eq "invalid" ) {
6032	     $status_bar->push($context_id, $langs{msg080} );
6033	     $button_save_tag->set_sensitive($false);
6034	     return $false;
6035	  }
6036	  $tooltips->set_tip( $button_save_tag, $langs{tip305} ) if $show_all_tooltips;
6037      }
6038      elsif ( $extension_input =~ /(mp4)/ ){ # for mp4 file
6039	  # get info to be saved on normalized file
6040          $status_bar->push($context_id, $langs{msg061} );
6041	  return $false;
6042      }
6043      else { # $extension_input = other
6044          $status_bar->push($context_id, "" );
6045          $button_save_tag->set_sensitive($false);
6046	  return $false;
6047      }
6048
6049      update_tags($title,$artist,$album,$comment,$year,$tn,$tt,$genre);
6050
6051      $button_save_tag->set_sensitive($true);
6052      $status_bar->push($context_id, $langs{msg070} );
6053
6054      #print "\n sub change_tag --->2 \$row = $row \$filepath = $filepath ;; title = $title ;; album = $album\n";
6055
6056      return $true;
6057}
6058
6059my %temp_tags; # the hash %temp_tags is a temporaty variables only used on sub change_tag
6060
6061sub update_tags {  # used by save_tag
6062   my ($title,$artist,$album,$comment,$year,$tn,$tt,$genre) = @_;
6063
6064   %temp_tags = ();
6065
6066   # the hash %temp_tags is a temporaty variables only used on 'sub change_tag'.
6067   $temp_tags{title}   = remove_change_10_chars($title);
6068   $temp_tags{artist}  = remove_change_10_chars($artist);
6069   $temp_tags{album}   = remove_change_10_chars($album);
6070   $temp_tags{comment} = remove_change_10_chars($comment);
6071   $temp_tags{genre}   = remove_change_10_chars($genre);
6072   $temp_tags{year}    = remove_change_10_chars($year);
6073   $temp_tags{track}   = remove_change_10_chars($tn);     # $Track_Number
6074   $temp_tags{total_track}  = remove_change_10_chars($tt);
6075
6076   #print "\n sub update_tags ---> Title = $temp_tags{title} ;; Artist = $temp_tags{artist} ;; Album = $temp_tags{album}\n";
6077}
6078
6079sub file_is_writable {
6080   # Test if the $filepath is writable.
6081   # '-w' file is writable by effective uid/gid.
6082   my $filepath = shift;
6083   if ( not -w filename_from_unicode $filepath ) {
6084      $status_bar->push($context_id, " $filepath ".$langs{msg012} );
6085      return $false;
6086   }
6087   return $true;
6088}
6089
6090#####-------------final of Combo---mp3 info--------#####
6091
6092
6093###----------------------table 31 -------------------###
6094
6095my $label_mpeg = new Gtk2::Label( "MPEG" );
6096#$label_mpeg->set_justify( 'left' );
6097$label_mpeg->set_alignment( 0, 0.5 );
6098$table31->attach ($label_mpeg, 0, 1, 1, 2, 'fill', ['expand','fill'], 0, 0 );
6099$label_mpeg->show();
6100#$label_mpeg->set_label ($str);
6101
6102my $label_kbps = new Gtk2::Label( "Bitrate:" );
6103$label_kbps->set_alignment( 0, 0.5 );
6104$table31->attach ($label_kbps, 0, 1, 2, 3, 'fill', ['expand','fill'], 0, 0 );
6105$label_kbps->show();
6106
6107my $label_freq = new Gtk2::Label( $langs{name041} . ":" );
6108$label_freq->set_alignment( 0, 0.5 );
6109$table31->attach ($label_freq, 0, 1, 3, 4, 'fill', ['expand','fill'], 0, 0);
6110$label_freq->show();
6111
6112my $label_mode = new Gtk2::Label( $langs{name031} . ":" );
6113$label_mode->set_alignment( 0, 0.5 );
6114$table31->attach ($label_mode, 0, 1, 4, 5, 'fill', ['expand','fill'], 0, 0);
6115$label_mode->show();
6116
6117my $label_time = new Gtk2::Label( $langs{name042} . ":" );
6118$label_time->set_alignment( 0, 0.5 );
6119$table31->attach ($label_time, 0, 1, 5, 6, 'fill', ['expand','fill'], 0, 0);
6120$label_time->show();
6121
6122my $label_size = new Gtk2::Label( $langs{name048} . ":" );
6123$label_size->set_alignment( 0, 0.5 );
6124$table31->attach ($label_size, 0, 1, 6, 7, 'fill', ['expand','fill'], 0, 0);
6125$label_size->show();
6126
6127$separator = Gtk2::HSeparator->new;
6128$table31->attach ( $separator, 0, 1, 7, 8, ['expand','fill'],['expand','fill'], 0, 0 );
6129$separator->show;
6130
6131
6132$button_save_tag = Gtk2::Button->new;
6133$button_save_tag->add( DrawIcons('gtk-save','button') );
6134#$tooltips->set_tip( $button_save_tag, "Save changes of tag to $extension_input file.");
6135$button_save_tag->signal_connect( "clicked", \&save_tag );
6136$table31->attach ($button_save_tag,  0, 1, 8, 9 , 'shrink', 'shrink', 0, 0);
6137$button_save_tag->set_sensitive($false);
6138$button_save_tag->set( 'focus-on-click' => $false, 'relief' => 'none' );
6139$button_save_tag->show();
6140
6141
6142###----------------id3 tag & mpeg info---------------###
6143######--------------------------------------------######
6144
6145###----------------------table 4 --------------------###
6146#--------------------------Rip-------------------------#
6147
6148# some buttons
6149my $hbox4 = Gtk2::HBox->new ($false, 2);
6150#$box->pack_start ($child, $expand, $fill, $padding)
6151$vbox4->pack_start ($hbox4, $false, $false, 0);
6152$hbox4->show;
6153
6154$label = new Gtk2::Label( " cdrom:" );
6155$label->set_alignment( 1.0, 0.5 );
6156$hbox4->pack_start ($label, $false, $false, 0);
6157$label->show();
6158
6159# Create the Entry to put the cdrom drive path
6160$entry_cda = new Gtk2::Entry();
6161$entry_cda->set_text($audiodevice_path);
6162$tooltips->set_tip( $entry_cda, $langs{tip401} ) if $show_all_tooltips;
6163$entry_cda->set_size_request(120);
6164$entry_cda->signal_connect("changed", \&entry_cda_change );
6165$hbox4->pack_start ($entry_cda, $false, $false, 0);
6166$entry_cda->show();
6167
6168$button = Gtk2::Button->new;
6169$button->add( DrawIcons('gtk-refresh','button') );
6170$button->set( 'focus-on-click' => $false, 'relief' => 'none' );
6171$tooltips->set_tip( $button, $langs{tip402} ) if $show_all_tooltips;
6172$button->signal_connect (clicked => \&refresh_cda);
6173$hbox4->pack_start ($button, $false, $false, 0);
6174$button->show;
6175
6176# my $button_selec = Gtk2::Button->new ("unselect all");
6177my $button_selec = Gtk2::Button->new;
6178$button_selec->add( DrawIcons('gtk-close','button') );
6179$button_selec->set( 'focus-on-click' => $false, 'relief' => 'none' );
6180$tooltips->set_tip( $button_selec, $langs{tip404} ) if $show_all_tooltips;
6181$button_selec->signal_connect (clicked => \&unselect_cda);
6182#$button_selec->set_sensitive($false);
6183$button_selec->show;
6184
6185my $button_unselec = Gtk2::Button->new;
6186$button_unselec->add( DrawIcons('gtk-cancel','button') );
6187$button_unselec->set( 'focus-on-click' => $false, 'relief' => 'none' );
6188$tooltips->set_tip( $button_unselec, $langs{tip403} ) if $show_all_tooltips;
6189$button_unselec->signal_connect (clicked => \&unselect_cda);
6190$button_unselec->set_sensitive($false);
6191$button_unselec->show;
6192
6193# this align is a container that hold two buttons alternately
6194my $align_selec = Gtk2::Alignment->new(0.5, 0.5 , 0, 0);
6195$align_selec->add ($button_unselec);
6196$hbox4->pack_start ($align_selec, $false, $false, 0);
6197$align_selec->show;
6198
6199# to fill the empty space
6200$label = new Gtk2::Label( " " );
6201$label->set_alignment( 0.5, 0.5 );
6202$hbox4->pack_start ($label, $true, $true, 0);
6203$label->show;
6204
6205
6206###---------------------- Display --------------------###
6207#------------------------- start -----------------------#
6208
6209# convert sound.png -resize 32x32 -enhance -colors 16 sound.xpm  or use gimp > Image > Mode > Indexed
6210# convert sound.xpm -resample 12x40 -colors 16 -enhance  sound2.xpm
6211# cat sound.xpm | sed "s/\\\$/S/g" | sed "s/\@/x/g"|sed "/pixels/d" >sound3.xpm
6212my @sound = (
6213# /* columns rows colors chars-per-pixel */
6214"160 186 41 1",
6215" 	c None",
6216".	c #2C2827",
6217"+	c #39312E",
6218"x	c #453331",
6219"#	c #413739",
6220"S	c #413A35",
6221"%	c #453935",
6222"&	c #483C38",
6223"*	c #46403A",
6224"=	c #4A4041",
6225"-	c #524641",
6226";	c #514647",
6227">	c #5C4750",
6228",	c #594A51",
6229"'	c #5A4D49",
6230")	c #594E4E",
6231"!	c #62535A",
6232"~	c #605555",
6233"{	c #645752",
6234"]	c #695A61",
6235"^	c #675C5D",
6236"/	c #716169",
6237"(	c #6F635E",
6238"_	c #6E6364",
6239":	c #766964",
6240"<	c #75696B",
6241"[	c #796E6E",
6242"}	c #7D7272",
6243"|	c #827676",
6244"1	c #867A7B",
6245"2	c #8B7F7F",
6246"3	c #8F8384",
6247"4	c #92837F",
6248"5	c #968A8A",
6249"6	c #9C9090",
6250"7	c #A29697",
6251"8	c #A99D9E",
6252"9	c #B0A3A4",
6253"0	c #B6AAAA",
6254"a	c #BCB0B0",
6255"b	c #DBD5D3",
6256"                                                                                                                                                                ",
6257"                                                                                                                                                                ",
6258"                                                                                 888888888888888888                                                             ",
6259"                                                                             88888888888888888888888888                                                         ",
6260"                                                                         888888888888888888888888888888888                                                      ",
6261"                                                                       88888888888888888888888888888888888888                                                   ",
6262"                                                                    8888888888888888888888888888888888888888888                                                 ",
6263"                                                                  88888888888888888876553333556788888888888888888                                               ",
6264"                                                                88888888888888863|[<<::::__(____<[13688888888888888                                             ",
6265"                                                              888888888888865|[<<<<<_({{{''''{{{^__((<158888888888888                                           ",
6266"                                                             8888888888885|<<<:_{'-&&&&&&&&&&&&&&&-''{((:1688888888888                                          ",
6267"                                                           8888888888861[<<:({-&&&&&&&&&&&&&&&&&&&&&&&&-{((<588888888888                                        ",
6268"                                                          88888888886|<<<_{-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-{^<38888888888                                       ",
6269"                                                        888888888861[<<('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-'^<5888888888                                      ",
6270"                                                       88888888872[[<{-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'(|788888888                                     ",
6271"                                                      8888888885[[[(-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-{_5888888888                                   ",
6272"                                                     888888886|[[:'&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-{|888888888                                  ",
6273"                                                   8888888885[[<{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'<688888888                                 ",
6274"                                                  8888888882[[_-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-{588888888                                ",
6275"                                                 888888886|[[{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&{288888888                               ",
6276"                                                888888885}[<'&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&'|88888888                              ",
6277"                                               888888883[[(*&&&&&&&&&&&&&&&&&&&&---------*SSSSSSSSS%&&&&&&&&&&&&&&&&&&&&&'|8888888                              ",
6278"                                              888888882[[{&&&&&&&&&&&&&&&&&'{{^________<<<<_~'*SSSSSSSS%&&&&&&&&&&&&&&&&&&-}8888888                             ",
6279"                                             888888881[['&&&&&&&&&&&&&&-{__^^^_________<<<<<<[[_{*SSSSSSSS%&&&&&&&&&&&&&&&&-|8888888                            ",
6280"                                            888888881[['&&&&&&&&&&&&&{<<^^^^^^^_________<<<<<[[[[[_'SSSSSSSSS&&&&&&&&&&&&&&&-18888888                           ",
6281"                                           888888881[<-&&&&&&&&&&&-(<_^^^^^^^^^^________<<<<<<[[[[[[<{*SSSSSSSS&&&&&&&&&&&&&&-38888888                          ",
6282"                                           88888881}[-&&&&&&&&&&-_[^^^^^^^^^^^^^^________<<<<<[[[[[[[[<'SSSSSSSS%&&&&&&&&&&&&&'5888888                          ",
6283"                                          88888882}[-&&&&&&&&&&^[^^^^^^^^^^^^^^^^^_______<<<<<[[[[[[[[[[_-SSSSSSSS%&&&&&&&&&&&&{6888888                         ",
6284"                                         88888883}[-&&&&&&&&&{[^^^^^^^^^^^^^^^^^^^________<<<<<[[[[[[[[[[[{SSSSSSSSS&&&&&&&&&&&&_8888888                        ",
6285"                                        88888885}['&&&&&&&&-<_~~~~~^^^^^^^^^^^^^^^^_______<<<<<[[[[[[[[[[[[_*SSSSSSSSS&&&&&&&&&&&}888888                        ",
6286"                                       88888886}['&&&&&&&&'<^~~~~~~~~~^^^^^^^^^^^^^_______<<<<<[[[[[[[[[[[[[<'SSSSSSSSS&&&&&&&&&&*5888888                       ",
6287"                                       8888886|}{&&&&&&&&(_~~~~~~~~~~~~~^^^^^^^^^^^^______<<<<<[[[[[[[[[[[[[[[{SSSSSSSSS&&&&&&&&&&'7888888                      ",
6288"                                      8888887|}{&&&&&&&-<^~~~~~~~~~~~~~~~^^^^^^^^^^^_______<<<<[[[[[[[[[[[[[[[[{SSSSSSSSS&&&&&&&&&&<888888                      ",
6289"                                     88888882}_&&&&&&&-_~~~~~~~~~~~~~~~~~~^^^^^^^^^^^______<<<<<[[[[[[[[[[[[[[[[{SSSSSSSSS&&&&&&&&&&3888888                     ",
6290"                                     8888885}<-&&&&&&'_~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^______<<<<<[[[[[[[[[[[[[[[[[^SSSSSSSSS&&&&&&&&&'888888                     ",
6291"                                    8888886|}'&&&&&&'^~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^______<<<<<[[[[[[[[[[[[[[[[[[~SSSSSSSSS&&&&&&&&&[888888                    ",
6292"                                   88888881}{&&&&&&'^~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^_____<<<<<[[[[[[[[[[[[[[[[[[[{SSSSSSSSS&&&&&&&&*6888888                   ",
6293"                                   8888883}(&&&&&&'~~~~~~~~~~~~~~)~~~~~~~~~~~^^^^^^^^^______<<<<[[[[[[[[[[[[[[[[[[[['SSSSSSSSS&&&&&&&&(888888                   ",
6294"                                  8888886}[-&&&&&'~~~~)~))))))))))~)~~~~~~~~~~^^^^^^^^______<<<<[[[[[[[[[[[[[[[[[[[[<*SSSSSSSSS&&&&&&&&5888888                  ",
6295"                                 8888888|}'&&&&&-~~~))))))))))))))))~~~~~~~~~~^^^^^^^^______<<<<[[[[[[[[[[[[[[[[[[[[[_*SSSSSSSSS&&&&&&&{888888                  ",
6296"                                 8888883}^&&&&&-~~~))))))))))))))))))~~~~~~~~~~^^^^^^^______<<<<[[[[[[[[[[[[[[[[[[[[[[{SSSSSSSSS%&&&&&&&5888888                 ",
6297"                                8888886|[&&&&&&~~~)))))))))'''))))))))~~~~~~~~~^^^^^^^^_____<<<<[[[[[[[[[[[[[[[[[[[[[[[-SSSSSSSSS&&&&&&&{888888                 ",
6298"                                8888882}'&&&&&)~~)))))))))''')))'))))))~~~~~~~~^^^^^^^^_____<<<<[[[[[[[[[[[[[[[[[[[[[[[<*SSSSSSSSS&&&&&&&588888                 ",
6299"                               8888885|_&&&&&)))))))))'''''''')'')'))))))~~~~~~~^^^^^^^_____<<<<[[[[[[[[[[[[[[[[[[[[[[[[^SSSSSSSSSS&&&&&&(888888                ",
6300"                               888888|}-&&&&-~))))))''''''''''''''))))))~~~~~~~~^^^^^^^_____<<<<[[[[[[[[[[[[[[[[[[[[[[[[[-SSSSSSSSS&&&&&&&688888                ",
6301"                              8888885|{&&&&*)))))''''''))))))))'''))')))))~~~~~~^^^^^^^_____<<<<[[[[[[[[[[[[[[[[[[[[[[[[[_SSSSSSSSSS&&&&&&<888888               ",
6302"                              888887|[-&&&%))))))''''))))))))))))''))))))~~~~~~~^^^^^^^____<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[['SSSSSSSSS%&&&&&-788888               ",
6303"                             8888883|{&&&%-)))'''')))))------))))))''))))))~~~~~^^^^^^^____<<<<[[[[[}}}}}}}}}}}}}[[[[[[[[[<SSSSSSSSSS&&&&&&288888               ",
6304"                             888888|[&&&&*)))''''))))-;;;;;;;;-))))'''')))~~~~~~~^^^^^^____<<<<[[[}}}}}}}}}}}}}}}}}[[[[[[[['SSSSSSSSSS&&&&&{888888              ",
6305"                          +%5888883|{&&&%)))''')'));;;;;;;;;;;;;-))''')))))~~~~~~^^^^^^____<<<<[[}}}}}}}}}}}}}}}}}}}}[[[[[[<*SSSSSSSSS&&&&&&688888              ",
6306"                       +++%{888887|[&&&%-)))'')))-;;;;;;;;;;;;;;;-)''''')))~~~~~~^^^^^^___<<<<[[}}}}}}}}}}}}}}}}}}}}}}}[[[[['SSSSSSSSSS&&&&&}88888              ",
6307"                      ++-:47888885|{&&&%))''')));;;;;;;;;;;;;;;;;;)))'')))))~~~~~^^^^^^___<<<<[[}}}}}}}|||||||}}}}}}}}}}[[[[_SSSSSSSSSS&&&&&'888888             ",
6308"                    ++'|444888888|[&&&%;)'''))-;;;;;;;;;;;;;;;;;;;;)))'')))~~~~~~^^^^^___<<<<[[[}}}||||||||||||||}}}}}}}}}[[[-SSSSSSSSS%&&&&&688888             ",
6309"                   +%:44447888885|{&&S&)'''))-;;;;;;;--;;;-;;;;;;;;-))''')))~~~~~^^^^^___<<<<[[}}|||||||||||||||||||}}}}}}}[[{%SSSSSSSSS&&&&&[88888             ",
6310"                  +-|444448888882}&&&&)'''))-;;;;;;-;--;;--;;;;;;;;;)''''))~~~~~~^^^^^___<<<[[}}|||||||||||||||||||||}}}}}}}[[*%SSSSSSSS%&&&&'888888            ",
6311"                 +'4444446888886|^&&%=)''))-;;;;;-;;;------;-;;;;;;;-)''))))~~~~~^^^^^__<<<<[[}}|||||||||||||||||||||||}}}}}}['%%SSSSSSSS&&&&&688888            ",
6312"                +'44444448888883|-&&%)''))-;;;;;;--;-=====---;=-===;;))'')))~~~~{^^^^___<<<[[}}||||||1111111111|||||||||}}}}}}_S%SSSSSSSS&&&&&188888            ",
6313"               +-44444445888887|(&&%=)'')-;;;;;;;--========S+........+S-))))~~~~~^^^____<<[[[}||||111111111111111||||||||}}}}}[*S%SSSSSSSS&&&&(888888           ",
6314"              +&444444446888885|'&%%'''));;;;---;=======#+...............S))~~~~~^^^___<<<[[}}||1111112222222211111|||||||}}}}}{%%%SSSSSSS&&&&-888888           ",
6315"             +%|444444448888881[&&S=''));;;;---=======#....................=)~~{^^^^___<<[[}}|||111222222222222221111||||||}}}}_SS%SSSSSSS&&&&&588888           ",
6316"            ++:44444446b888887|{&S%)')';;;;---=====-*+......................+)~^^^^^__<<<[[}|||11222222444442222221111||||||}}}[-%S%SSSSSSS&&&&|88888           ",
6317"            +'44444444ba888885}-&S&'')-;;;---====-&+..........................){^^^___<<[[}}||1122222343333333242222111|||||}}}}'SS%SSSSSSS&&&&(888888          ",
6318"           +%44444444ba7888881<&&%;'');;;;-;====**+............................)^^^___<<[}}||112222333333333333322222111|||||}}}^S%%%SSSSSS&&&&'888888          ",
6319"           +(4444444ab/888886|{&S%'));;;;;-===-**..............................+)^___<<[[}|||1222333333333333333342222111||||}}}<*SS%SSSSSS%&&&&688888          ",
6320"          +-44444448b<<888883|-&%=')-;;;;-===-&*...............................++~__<<<[}}||12233333555555555533333222211|||||}}[-SS%SSSSSSS&&&&588888          ",
6321"          +:4444444b7>5888881:&%%'));;;-;-==-&*...............................+++S^_<<[[}||1122333555555555555553333232211||||}}}'*S%%SSSSSS&&&&|88888          ",
6322"         +-4444444ab>>888887|{&%&');;;;-;==-&*................................++++;<<<[}}||12233355555555555555555333222111||||}}{*SS%SSSSSS&&&&_88888          ",
6323"         +:4444445b|>/888885|-%+-'-;;;;-===&&+...............................+++++#^<[[}||123335555555565555555555533322211||||}}_*SS%SSSSSSS&&&'888888         ",
6324"        +%4444444aa>>1888882<&%%));;;-;===*&+...............................+++++++=<[}||11223555556666666666655555533322211||||}[&*SS%SSSSS%&&&-888888         ",
6325"        +{4444446b/>>688887|{&%='-;;;-;==*&S.................+##====#++.....+++++#SS^[}||12333555566666666666666555553322211||||}}-*SS%SSSSSS&&&&788888         ",
6326"        +|444444aa>>!888886|-%+;);;;--==-*&................#;,,,,,,,,,,=#..++++++#SS;[||1223355566666666666666666555433342211|||}}'&SS%SSSSSS&&&&588888         ",
6327"       +-4444444b|>>/888885[&%%)-;;;--==&&+..............#,,,,,,,,,,,,,,,,#+++++#SS==_||1223555666666677777766666655553332211||||}{&*%%SSSSSS&&&&188888         ",
6328"       +(4444449b>>>1888882(&+&);;;;-==*&S.............#,,,,,,,,,,,,,,,,,,,,+++#SSS==~|12333556666677777777777666665555332211||||}{&*S%%SSSSS&&&&[88888         ",
6329"       +|444444b7>>>588888|{%+=-;;;--==&=.............=,,,,,,,,,,,,,,,,,,,,,,#++SS===;|123355666667777777777777666665553322211|||}(**S%%SSSSSS&&&_88888         ",
6330"      +%4444444b/>>>788886|-%+;;;;;-==*=S..........++;!!!!!!,,,,,,,,,,,,,,,,!!=SSS==;;<223555666677777888887777766665555332211|||}:&&SS%SSSSS%&&&{888888        ",
6331"      +'4444440b>>>]888885}&SS-;;;;===*&.........++#;]]]]]]!!!,,,,,,,,,,,,,!!!!=S===;;^233556667777888888888877776666555332211||||[&*SS%SSSSSS&&&'888888        ",
6332"      +(444444b8>>><888883_S+&;;;;-==**S........++S;////////]]!!,,,,,,,,,,,!!!!!===;;)~2355566777788888888888887776665553322211|||[-&*%%SSSSSS&&&-888888        ",
6333"      +|444444b1>>>3888881{%+=;;;--==*=+.......++#;/<|11111<//]]!,,,,,,,,,!!!!!]!==;;))1355667778888888998888888777666555332211|||}-&&%SSSSSS%&&&*888888        ",
6334"     +%4444446b!>>>6888871'%+;;;-;==*&#.......++S=]<133553331<//]!!,,,,,,,!!!!!]]);;))~|556667788888999999998888777666555332211|||}'**S%SSSSSS&&&&788888        ",
6335"     +-4444440b>>>>888886|-%S;;;;;==&&+.......++=)<13577777531|//]!!,,,,,,!!!!]]]];;))~[556677888899999999999888877766655332211|||}'**SSSSSSSS&&&&688888        ",
6336"     +'444444a0>>>!888885[&+%;;;;==-&=........+#=/|3578999977531//]!!,,,,!!!!!]]]/~))~~<566777888999999999999988877766655332211|||}'&&SSSSSSSS&&&&588888        ",
6337"     +{444444b6>>>/888883_S+&;;;-==*&#........+#;/13679000098751|/]!!,,,,!!!!!]]///))~^<566778889999999999999998887766655533221||||'*&SSSSSSSS&&&&588888        ",
6338"     +(444444b1>>>|888882{%+-;;-;==*=+........+S!/136790aaa098731</]!!,,,!!!!]]]///~~~^<6677888999999900009999998887766555332211|||{*&SS%SSSSS&&&&588888        ",
6339"     +:444444b/>>>1888881'S+;;;;==-&&+........+#]/|357900aa009753|/]!!,,!!!!!]]]///^~~^<667788999999000000099999888776655533221||||{&*SS%SSSSS&&&&388888        ",
6340"     +}444446b!>>>5888871-%%;;--==*&&.........+#]/<1367900a009753|/]!!,,!!!!!]]/////~^^<6678889999000000000099999887766655332211|||{&*SS%SSSSS&&&&388888        ",
6341"     +|444446b!>>>688887|&+%;;;;==&=#..........=!]/<1367990099753|/]!!!!!!!!]]]////<^^^<6778899990000000000009999887766655332211|||{&*SS%SSSSS&&&&388888        ",
6342"     +|444447b>>>>788886[*+%;;--==&&+..........=!!]/|136779887631</]!!!!!!!!]]]////<^^_<6788899900000000000000999888776655332211|||{&*SS%SSSSS&&&&388888        ",
6343"     +|444448b>>>!888885<%+=;;;==-&=+..........,,!]//<1335676531|//]!!!!!!!!]]////<<_^_[6788999900000aaaa00000999988776655532211|||{**SS%SSSSS&&&&388888        ",
6344"     +|444448b>>>]888885(S+=;--==*&=+..........;,,!!]//|1133311|//]!!!,!!!!!]]////<<<^_}788899900000aaaaaa0000099988776655532211|||'**SS%SSSSS&&&&588888        ",
6345"     +|444448b>>>/888883{S+=;;-==&&&...........;,,,!!]///<|||<///]!!!,,!!!!]]]////<|<_<|78899900000aaaaaaaa000099988776655532211|||'&*SS%SSSSS&&&&588888        ",
6346"     +:444448b>>>/888882{%+;;;;==&&%...........,,,,,!!!]]/////]]!!!!,,,!!!!]]]////<|<_<17889990000aaaaaaaaa000099988776655532211|||'&*SS%SSSS%&&&&588888        ",
6347"     +:444447b>>><888882'%+;;--==*=#...........,,,,,,,,!!!!!!!!!!!,,,,,!!!!]]]///<<|<_<3788999000aaaaaaaaaaa00099988776655532211||}-&*SS%SSSSS&&&&688888        ",
6348"     +(444446b!>>1888881'%%;;;==-*=#...........=,,,,,,,,,,,!!,,,,,,,,,!!!!!]]////<<|<_<5889990000aaaaaaaaaaa00099988776655532211||}-&*SS%SSSS%&&&&688888        ",
6349"     +'444444b/>>1888881-+%;-;==-&=#...........#,,,,,,,,,,,,,,,,,,,,,,!!!!!]]////<|1<_<688999000aaaaaaaaaaaaa0099988776655532211||}-*&SSSSSSS%&&&&788888        ",
6350"     +-444444b|>>388888|-+%;-;==-&=#...........#,,,,,,,,,,,,,,,,,,,,,,!!!!]]]////<||_<[788999000aaaaaaaaaaaa0009998877665533421|||[&*&SSSSSSS&&&&-888888        ",
6351"     +%444444b3>>388888}&+%;;-==-&=#...........+,,,,,,,,,,,,,,,,,,,,,,!!!!]]]////<||_<|788999000aaaaaaaaaaaa00099988776655332211||:&&*SSSSSSS&&&&-888888        ",
6352"      +|44444b9>>388887}&+%;;-==-*&%............;,,,,,,,,,,,,,,,,,,,,,!!!!]]]////<|<_<3788999000aaaaaaaaaaaaa009998877665433221|||(&**S%SSSSS&&&&'888888        ",
6353"      +(44444ab>>588887[&+%;;-==-*&#............#,,,,,,,,,,,,,,,,,,,,,!!!!]]]///<<|__<688999000aaaaaaaaaaaaa0009998877665533221|||{&**SSSSSSS&&&&{888888        ",
6354"      +-444447b>>588887[%+%;;-==-&&&............+,,,,,,,,,,,,,,,,,,,,,!!!!]]]///<<<^_[788999000aaaaaaaaaaaaa0009998876665533221|||{&&&%%SSSS%&&&&(88888         ",
6355"      +%444444b<>588887[S+%;;-===&*&.............=,,,,,,,,,,,,,,,,,,,,!!!!]]]///<<_^_17889990000aaaaaaaaaaaa0009998776655533211|||'*&*S%SSSS%&&&&[88888         ",
6356"       +(44444b8>588887<%+%;;-===*&=+............+,,,,,,,,,,,,,,,,,,,,!!!!]]]///<<^^_57889999000aaaaaaaaaaa00009998876655532211|||-&*SS%SSSSS&&&&188888         ",
6357"       +&444440b>588887<%+%;-;===**&+.............=,,,,,,,,,,,,,,,,,,,!!!!]]]///<^^^[67889999000aaaaaaaaaaa00099988776655334211||[**&SS%SSSS&&&&&588888         ",
6358"        +:44444b/588887<%+%;-;-==-&&S..............;,,,,,,,,,,,,,,,,,,!!!!]]]///_~^^2778889990000aaaaaaaaa00009998877665533221|||:&&*S%%SSSS&&&&&688888         ",
6359"        +-44444b8588887<%+%;--;===***..............+,,,,,,,,,,,,,,,,,,!!!!]]]///~~^_6677889990000aaaaaaaaa00099998876665533221|||{&**%SSSSSS&&&&*888888         ",
6360"         +(44449b788887<&+%;;--===-*&...............#,,,,,,,,,,,,,,,,,!!!!]]]//~)~^|66778899900000aaaaaaa000099988776655533211|||'*&*%%SSSS%&&&&'888888         ",
6361"         +x|4444b088887<&+%;;;-====-*+...............#,,,,,,,,,,,,,,,,!!!!]]]])))~^5667788999900000aaaa00000999988776655332211||}-&*SS%SSSS&&&&&(888888         ",
6362"          +-4444aa88888[&%%;;;;;====-S................#;,,,,,,,,,,,,,,!!!!]]]);))~}6667788999900000000000000999887766655332211||[**&SS%SSSS&&&&&[88888          ",
6363"           +-4444088888[&%%;;---=====&.................+;,,,,,,,,,,,,,!!!!])=;;))^5666778889999000000000000999988776655533221|||(&&*S%SSSS%&&&&&288888          ",
6364"            +-444788888}&S%;;;;;;=====+..................#,,,,,,,,,,,,!!!,===;;))15566777889999900000000009999887776655332211|||{&&*%%SSSS%&&&&&688888          ",
6365"             +&:4788888|&S+;;;----=====....................#=,,,,,,,,,,;=S#==;;)_55566677888999990000000099999887766655332211||}-&*SS%SSSS&&&&&-788888          ",
6366"              ++-3888881*S+;;;;;;-;====+......................+##===##++#SS===;~25556667788899999900000999999887766655533221|||[-&&SS%SSSS&&&&&{888888          ",
6367"               ++{888882-%+=;;;;;;--;--=............................++++#SS===;|35556667778889999999999999998887766655332211|||(&**S%SSSS%&&&&&<88888           ",
6368"                 _888883-%+=;;;;;;-;;---S...........................+++++SSS==<233555666778889999999999999988877666555332211|||'&&S%%SSSS&&&&&&388888           ",
6369"                  888885'&+&;;;;;;-;;---;+..........................++++++SS=^223355566677788899999999999988877766555332211|||}-&&SS%SSSS&&&&&&688888           ",
6370"                  888886{&%%;;;;;;;;;--;;=..........................++++++SS~1223355556667788888999999998888777666555332211|||(&**S%SSSS%&&&&&'888888           ",
6371"                  888886{&SS;;;;;;;;;;;;;;=..........................++++#S~|122333556666777888888999988888777666655332221||||{&*S%%SSSS&&&&&&(888888           ",
6372"                  888888(&S+-;;;;;;;;;;;;;;S.........................++++#~||122233555666677788888888888887776666555332211|||}-&&SS%SSSS&&&&&&288888            ",
6373"                  888888<&%+--;;;;;;;;;;;;;;S........................++++^}||11223355556666777788888888887776666555332221||||(*&*S%SSSSS&&&&&&688888            ",
6374"                  888888}&&+-)-;;;;;;;;;;;-))=.......................++=_}}||11223334556666677777888887777766665543332211|||}'**SS%SSSS&&&&&&'888888            ",
6375"                   888882&&S&)))--;;;;;;-))))';+.....................+)<[}}||1122233555566666777777777777766666555332221||||[***SS%SSS%&&&&&&[88888             ",
6376"                   888885-&S%''))))))))))))'''''=..................+;_<[[}||||112233355556666677777777776666665553322211|||}{**S%%SSSS&&&&&&&588888             ",
6377"                   888886'&%+)))'))))))')'''')')))=+.............+;^<<<[[}||||11222335555566666677777766666665553332211||||}-&*SS%SSS%&&&&&&-888888             ",
6378"                   888887{&&%-''')))))'''')''))))~~~)=+......+S;~__<<<<[[}|||||1122333555556666666666666666555533322211|||}:&*S%%SSSS&&&&&&&_888888             ",
6379"                   888888<&&S&)'''''''''''))))))~~~~~~~~~~~~^^^____<<<<[[}}||||112223335555566666666666666555553322211||||}'**SS%SSS%&&&&&&&388888              ",
6380"                    888881&&%%'''')''''))')))))~~~~~~~~^^^^^^^^____<<<[[}}}|||||1122233355555666666666665555553322211||||}:&*S%%SSSS&&&&&&&-788888              ",
6381"                    888885&&&%-'))))))')))))))~~~~~~~~^^^^^^^^_____<<<[[}}}}||||1122233335555555656555555545333222111||||}{**SS%SSS%&&&&&&&<888888              ",
6382"                    888886'&&%&)))))))))))))~~~~~~~~~^^^^^^^^_____<<<<[[}}}}|||||11224333355555555555555555333232211||||}<&*S%%SSSS&&&&&&&&588888               ",
6383"                    888888{&&%%)))))))))))~)~~~~~~~~^^^^^^^^______<<<[[[}}}}}||||1112223333555555555554553333222211||||}}{&*SS%SSS%&&&&&&&'888888               ",
6384"                    888888[&&&%'))))))))~~~~~~~~~~~^^^^^^^^______<<<<[[[[}}}}|||||11122433333555555555533333222211|||||}<-&SS%SSSS&&&&&&&&}888888               ",
6385"                     888883&&&%=)))))~~)~~~~~~~~~~^^^^^^^^^_____<<<<[[[[[}}}}}|||||112222333333335533333333222211|||||}}{*SSS%SSS&&&&&&&&&688888                ",
6386"                     888886-&&&%)~~))~~~~~~~~~~~~^^^^^^^^^_____<<<<[[[[[[[}}}}|||||11122222333333333333332222111|||||}}:**SS%SSSS&&&&&&&&(888888                ",
6387"                     888888^&&&%-~~~~~~~~~~~~~~^^^^^^^^^^______<<<<[[[[[[[}}}}}||||||11122222333333333422222111|||||}}}'&S%%%SSS&&&&&&&&&3888888                ",
6388"                     888888|&&&&*~~~~~~~~~~~~~^^^^^^^^^^______<<<<[[[[[[[[[}}}}}||||||111222222422242222222111||||||}}_*SSS%SSS&&&&&&&&&'888888                 ",
6389"                      888885*&&&S)~~~~~~~~~~~^^^^^^^^^^______<<<<<[[[[[[[[[}}}}}}||||||111122222222222222111|||||||}}[-SSS%SSS%&&&&&&&&&1888888                 ",
6390"                      888888{&&&&-~~~~~~~~~^^^^^^^^^^^______<<<<<[[[[[[[[[[[}}}}}}||||||1111112222222211111|||||||}}}{*SS%SSSS&&&&&&&&&-888888                  ",
6391"                      888888[&&&&S~~~~~~~^^^^^^^^^^^^______<<<<<[[[[[[[[[[[[[}}}}}}|||||||111111111111111|||||||}}}}<*SS%%SSS&&&&&&&&&&[888888                  ",
6392"                       888885&&&&&-~~~~^^^^^^^^^^^^^_______<<<<[[[[[[[[[[[[[[[}}}}}}|||||||||1111111111||||||||}}}}[-SSS%SSSS&&&&&&&&&-788888                   ",
6393"                       888888{&&&&&~~^^^^^^^^^^^^^^_______<<<<<[[[[[[[[[[[[[[[[}}}}}}|||||||||||||||||||||||||}}}}}{SSS%SSSS&&&&&&&&&&[888888                   ",
6394"                       888888|&&&&&-^^^^^^^^^^^^^^_______<<<<<[[[[[[[[[[[[[[[[[[}}}}}}||||||||||||||||||||||}}}}}}<&%S%SSSS&&&&&&&&&&-788888                    ",
6395"                        888887&&&&&&~^^^^^^^^^^^^_______<<<<<[[[[[[[[[[[[[[[[[[[[}}}}}}}||||||||||||||||||}}}}}}}[-SS%SSSS&&&&&&&&&&&[888888                    ",
6396"                        888888(&&&&&-^^^^^^^^^^________<<<<<[[[[[[[[[[[[[[[[[[[[[[}}}}}}}}||||||||||||||}}}}}}}}['SS%SSSS&&&&&&&&&&&-7888888                    ",
6397"                         888883&&&&&&~^^^^^^^^________<<<<<[[[[[[[[[[[[[[[[[[[[[[[[}}}}}}}}}}||||||||}}}}}}}}}}[^%S%SSSS&&&&&&&&&&&&|888888                     ",
6398"                         888888'&&&&&-_^^^^^^________<<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[}}}}}}}}}}}}}}}}}}}}}}}}}}[_&S%SSSS&&&&&&&&&&&&'788888                      ",
6399"                         888888|&&&&&&{_^^^_________<<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[}}}}}}}}}}}}}}}}}}}}}}[[<&%%SSSS&&&&&&&&&&&&&2888888                      ",
6400"                          888887-&&&&&&(___________<<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[}}}}}}}}}}}}}}}}}}[[[[-%%SSSS&&&&&&&&&&&&&{888888                       ",
6401"                          888888[&&&&&&*[________<<<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[}}}}}}}}}}}}[[[[[[-%SSSSSS&&&&&&&&&&&&&6888888                       ",
6402"                           888887-&&&&&&'}______<<<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['%SSSSSS&&&&&&&&&&&&&[888888                        ",
6403"                           888888[&&&&&&&{|____<<<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['SSSSSSS&&&&&&&&&&&&&'7888888                        ",
6404"                            888887-&&&&&&&_|__<<<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['SSSSSS&&&&&&&&&&&&&&&3888888                         ",
6405"                            888888}&&&&&&&&[|<<<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<'SSSSSS&&&&&&&&&&&&&&&[888888                          ",
6406"                             888887'&&&&&&&&[1<<<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<-SSSSSS&&&&&&&&&&&&&&&'8888888                          ",
6407"                             8888882&&&&&&&&&}2<<[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<*SSSSS%&&&&&&&&&&&&&&&&5888888                           ",
6408"                              888888{&&&&&&&&&[3[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[^*SSSSS&&&&&&&&&&&&&&&&&18888888                           ",
6409"                              8888886&&&&&&&&&&<3|[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{SSSSS%&&&&&&&&&&&&&&&&&(8888888                            ",
6410"                               888888}&&&&&&&&&&{32[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<-SSSS%&&&&&&&&&&&&&&&&&&'8888888                             ",
6411"                               8888888{&&&&&&&&&&'23|[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{*SSSS%&&&&&&&&&&&&&&&&&&-6888888                              ",
6412"                                8888886-&&&&&&&&&&&[52}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[_-SSSS%&&&&&&&&&&&&&&&&&&&&58888888                              ",
6413"                                 8888882&&&&&&&&&&&&{252}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<{SSSS%&&&&&&&&&&&&&&&&&&&&&18888888                               ",
6414"                                 8888888<&&&&&&&&&&&&&_352}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<{*SSS%&&&&&&&&&&&&&&&&&&&&&&}8888888                                ",
6415"                                  8888888{&&&&&&&&&&&&&-_353|[[[[[[[[[[[[[[[[[[[[[[[[[[[[<{*SSS&&&&&&&&&&&&&&&&&&&&&&&&<8888888                                 ",
6416"                                   8888886'&&&&&&&&&&&&&&-(1552|[[[[[[[[[[[[[[[[[[[[[[<_'SSS&&&&&&&&&&&&&&&&&&&&&&&&&&(88888888                                 ",
6417"                                   88888885-&&&&&&&&&&&&&&&&'<15531|}[[[[[[[[[[[[[[<_'*S%&&&&&&&&&&&&&&&&&&&&&&&&&&&&_88888888                                  ",
6418"                                    88888883&&&&&&&&&&&&&&&&&&&-{[135332211||||}<{'&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&(88888888                                   ",
6419"                                     88888882&&&&&&&&&&&&&&&&&&&&&&&-'{{{{{{'-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&:88888888                                    ",
6420"                                      88888881&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[88888888                                     ",
6421"                                      788888881&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-188888888                                      ",
6422"                                       788888882&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-588888888                                       ",
6423"                                        888888885-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&{688888888                                        ",
6424"                                         888888886'&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[888888888                                         ",
6425"                                          888888887(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-3888888888                                          ",
6426"                                           8888888881*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&_7888888888                                           ",
6427"                                            7888888886{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'3888888888                                             ",
6428"                                             78888888881-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-}7888888888                                              ",
6429"                                              78888888886<&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&<68888888888                                               ",
6430"                                                88888888886(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&-:68888888888                                                 ",
6431"                                                 788888888886<-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'|788888888888                                                  ",
6432"                                                  78888888888882{*&&&&&&&&&&&&&&&&&&&&&&&&&&&-<5888888888888                                                    ",
6433"                                                    788888888888872(-&&&&&&&&&&&&&&&&&&&&&{[588888888888888                                                     ",
6434"                                                     788888888888888861:{'-&&&&&&&&&-'(}37888888888888888                                                       ",
6435"                                                       788888888888888888886655555678888888888888888888                                                         ",
6436"                                                         78888888888888888888888888888888888888888888                                                           ",
6437"                                                           7788888888888888888888888888888888888888                                                             ",
6438"                                                             77888888888888888888888888888888888                                                                ",
6439"                                                                77888888888888888888888888887                                                                   ",
6440"                                                                     77888888888888888887                                                                       ",
6441"                                                                                                                                                                ");
6442
6443my $pixbuf_sound = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@sound);
6444# $pixbuf_sound->scale_simple ($dest_width, $dest_height, $interp_type)
6445my $sound_size_w = 14; # (36 46) scale the pixbuf_sound button
6446my $sound_size_h = 16;
6447$pixbuf_sound = $pixbuf_sound->scale_simple ( $sound_size_w, $sound_size_h, 'bilinear'); # hyper, bilinear
6448
6449
6450my @random = (
6451# /* columns rows colors chars-per-pixel */
6452"200 200 3 1",
6453" 	c None",
6454".	c $color_cdplayer_arrows",
6455"+	c $color_cdplayer_shadow",
6456"                                                                                                                                                                                                        ",
6457"                                                                                                                                                                                                        ",
6458"                                                                                                                                                                                                        ",
6459"                                                                                                                                                                                                        ",
6460"                                                                                                                                                                                                        ",
6461"                                                                                      ............................                                                                                      ",
6462"                                                                                 ......................................                                                                                 ",
6463"                                                                             ..............................................                                                                             ",
6464"                                                                         ......................................................                                                                         ",
6465"                                                                      ............................................................                                                                      ",
6466"                                                                   ..................................................................                                                                   ",
6467"                                                                ........................................................................                                                                ",
6468"                                                              ............................................................................                                                              ",
6469"                                                            ................................................................................                                                            ",
6470"                                                         ..............................++++++++++++++++++++++++++..............................                                                         ",
6471"                                                        ..........................++++++++++++++++++++++++++++++++++++..........................                                                        ",
6472"                                                      ........................++++++++++++++++++++++++++++++++++++++++++++........................                                                      ",
6473"                                                    ......................++++++++++++++++++++++++++++++++++++++++++++++++++++......................                                                    ",
6474"                                                  .....................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.....................                                                  ",
6475"                                                 ....................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++....................                                                 ",
6476"                                               ...................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...................                                               ",
6477"                                              ..................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..................                                              ",
6478"                                            ..................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..................                                            ",
6479"                                           .................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.................                                           ",
6480"                                          ................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++................                                          ",
6481"                                         ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                         ",
6482"                                       ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                       ",
6483"                                      ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                      ",
6484"                                     ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                     ",
6485"                                    ..............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                   ",
6486"                                   ..............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..............                                   ",
6487"                                  .............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.............                                  ",
6488"                                ..............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..............                                ",
6489"                                .............++++++++++++++++++++++++++++++++++++++++++++......................++++++++++++++++++++++++++++++++++++++++++++.............                                ",
6490"                               .............++++++++++++++++++++++++++++++++++++++++.................................+++++++++++++++++++++++++++++++++++++++.............                               ",
6491"                              ............++++++++++++++++++++++++++++++++++++++.........................................+++++++++++++++++++++++++++++++++++++.............                             ",
6492"                             ............++++++++++++++++++++++++++++++++++++...............................................+++++++++++++++++++++++++++++++++++............                             ",
6493"                            .............++++++++++++++++++++++++++++++++++...................................................+++++++++++++++++++++++++++++++++.............                            ",
6494"                           ............++++++++++++++++++++++++++++++++++........................................................++++++++++++++++++++++++++++++++............                           ",
6495"                          ............+++++++++++++++++++++++++++++++++............................................................+++++++++++++++++++++++++++++++............                          ",
6496"                          ............++++++++++++++++++++++++++++++++..............................................................++++++++++++++++++++++++++++++............                          ",
6497"                         ...........++++++++++++++++++++++++++++++++..................................................................++++++++++++++++++++++++++++++...........                         ",
6498"                        ...........+++++++++++++++++++++++++++++++.....................................................................++++++++++++++++++++++++++++++...........                        ",
6499"                       ............++++++++++++++++++++++++++++++........................................................................++++++++++++++++++++++++++++............                       ",
6500"                      ............++++++++++++++++++++++++++++++..........................................................................++++++++++++++++++++++++++++............                      ",
6501"                      ...........++++++++++++++++++++++++++++++............................................................................++++++++++++++++++++++++++++...........                      ",
6502"                     ...........++++++++++++++++++++++++++++++..............................................................................++++++++++++++++++++++++++++...........                     ",
6503"                    ...........++++++++++++++++++++++++++++++................................................................................++++++++++++++++++++++++++++...........                    ",
6504"                    ...........+++++++++++++++++++++++++++++..................................................................................+++++++++++++++++++++++++++...........                    ",
6505"                   ...........+++++++++++++++++++++++++++++....................................................................................+++++++++++++++++++++++++++...........                   ",
6506"                  ...........+++++++++++++++++++++++++++++......................................................................................+++++++++++++++++++++++++++...........                  ",
6507"                  ...........++++++++++++++++++++++++++++.......................................................................................+++++++++++++++++++++++++++...........                  ",
6508"                 ...........+++++++++++++++++++++++++++++........................................................................................+++++++++++++++++++++++++++...........                 ",
6509"                 ..........+++++++++++++++++++++++++++++.........................................................................................++++++++++++++++++++++++++++..........                 ",
6510"                ..........+++++++++++++++++++++++++++++...........................................................................................++++++++++++++++++++++++++++..........                ",
6511"                ..........+++++++++++++++++++++++++++++........................................+++++++++++........................................++++++++++++++++++++++++++++..........                ",
6512"               ..........+++++++++++++++++++++++++++++.....................................++++++++++++++++++++....................................++++++++++++++++++++++++++++..........               ",
6513"              ...........++++++++++++++++++++++++++++....................................++++++++++++++++++++++++..................................++++++++++++++++++++++++++++...........              ",
6514"              ..........+++++++++++++++++++++++++++++..................................+++++++++++++++++++++++++++.................................+++++++++++++++++++++++++++++..........              ",
6515"              ..........+++++++++++++++++++++++++++++................................+++++++++++++++++++++++++++++++................................++++++++++++++++++++++++++++..........              ",
6516"             ..........+++++++++++++++++++++++++++++................................+++++++++++++++++++++++++++++++++...............................+++++++++++++++++++++++++++++..........             ",
6517"             ..........+++++++++++++++++++++++++++++...............................+++++++++++++++++++++++++++++++++++..............................+++++++++++++++++++++++++++++..........             ",
6518"            ..........+++++++++++++++++++++++++++++...............................+++++++++++++++++++++++++++++++++++++..............................+++++++++++++++++++++++++++++..........            ",
6519"            ..........+++++++++++++++++++++++++++++..............................+++++++++++++++++++++++++++++++++++++++.............................+++++++++++++++++++++++++++++..........            ",
6520"           ..........++++++++++++++++++++++++++++++.............................++++++++++++++++++++++++++++++++++++++++.............................++++++++++++++++++++++++++++++..........           ",
6521"           ..........+++++++++++++++++++++++++++++.............................++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++..........           ",
6522"           .........++++++++++++++++++++++++++++++.............................++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++.........           ",
6523"          ..........++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++.............................++++++++++++++++++++++++++++++..........          ",
6524"          ..........++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++..........          ",
6525"          .........++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++.........          ",
6526"         ..........++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++..........         ",
6527"         .........+++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++.........         ",
6528"         .........+++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++..........        ",
6529"        ..........++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++...........................+++++++++++++++++++++++++++++++++..........        ",
6530"        .........+++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++...........................++++++++++++++++++++++++++++++++++.........        ",
6531"        .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++.........        ",
6532"        .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++.........        ",
6533"       ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++..........       ",
6534"       .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.............................+++++++++++++++++++++++++++++++++++.........       ",
6535"       .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++.........       ",
6536"       .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.............................++++++++++++++++++++++++++++++++++++.........       ",
6537"      ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.............................+++++++++++++++++++++++++++++++++++++..........      ",
6538"      .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.............................++++++++++++++++++++++++++++++++++++++.........      ",
6539"      .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..............................++++++++++++++++++++++++++++++++++++++.........      ",
6540"      .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..............................+++++++++++++++++++++++++++++++++++++++.........      ",
6541"      .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............................+++++++++++++++++++++++++++++++++++++++.........      ",
6542"     ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++................................++++++++++++++++++++++++++++++++++++++++..........     ",
6543"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++................................++++++++++++++++++++++++++++++++++++++++++.........     ",
6544"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.................................++++++++++++++++++++++++++++++++++++++++++.........     ",
6545"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.................................+++++++++++++++++++++++++++++++++++++++++++.........     ",
6546"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..................................++++++++++++++++++++++++++++++++++++++++++++.........     ",
6547"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..................................+++++++++++++++++++++++++++++++++++++++++++++.........     ",
6548"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...................................++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6549"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...................................+++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6550"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...................................++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6551"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++....................................+++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6552"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++....................................++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6553"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++....................................+++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6554"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.....................................++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6555"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++....................................++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6556"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++....................................+++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6557"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++....................................++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6558"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...................................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6559"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...................................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6560"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...................................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6561"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..................................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6562"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.................................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6563"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.................................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6564"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++................................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6565"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6566"     .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6567"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6568"     .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........     ",
6569"     ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........     ",
6570"      .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........      ",
6571"      .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........      ",
6572"      .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........      ",
6573"      .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........      ",
6574"      ..........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........      ",
6575"       .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........       ",
6576"       .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........       ",
6577"       .........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........       ",
6578"       ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........       ",
6579"        .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........        ",
6580"        .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........        ",
6581"        .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........        ",
6582"        ..........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........        ",
6583"         .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........        ",
6584"         .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........         ",
6585"         ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........         ",
6586"          .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........          ",
6587"          ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........          ",
6588"          ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........          ",
6589"           .........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.........           ",
6590"           ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........           ",
6591"           ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........           ",
6592"            ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........            ",
6593"            ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........            ",
6594"             ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........             ",
6595"             ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........             ",
6596"              ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........              ",
6597"              ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........              ",
6598"              ...........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........              ",
6599"               ..........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........               ",
6600"                ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........                ",
6601"                ..........++++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........                ",
6602"                 ..........+++++++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..........                 ",
6603"                 ...........++++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                 ",
6604"                  ...........+++++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                  ",
6605"                  ...........+++++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                  ",
6606"                   ...........++++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                   ",
6607"                    ...........+++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                    ",
6608"                    ...........+++++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                    ",
6609"                     ...........++++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                     ",
6610"                      ...........+++++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                      ",
6611"                      ............++++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++++............                      ",
6612"                       ............+++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++............                       ",
6613"                        ...........+++++++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++++++...........                        ",
6614"                         ...........++++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++++............                        ",
6615"                          ............++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++............                          ",
6616"                          ............++++++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++++++............                          ",
6617"                           ............+++++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++++............                           ",
6618"                            .............+++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++.............                            ",
6619"                             ............+++++++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++++++............                             ",
6620"                             .............++++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++++.............                             ",
6621"                               .............++++++++++++++++++++++++++++++++++++++............................++++++++++++++++++++++++++++++++++++++++++++++.............                               ",
6622"                                .............+++++++++++++++++++++++++++++++++++++............................+++++++++++++++++++++++++++++++++++++++++++++.............                                ",
6623"                                ..............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..............                                ",
6624"                                  .............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.............                                  ",
6625"                                   ..............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..............                                   ",
6626"                                   ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                   ",
6627"                                     ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                     ",
6628"                                      ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                      ",
6629"                                       ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                       ",
6630"                                         ...............++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...............                                         ",
6631"                                          ................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.................                                         ",
6632"                                           .................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.................                                           ",
6633"                                            ..................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..................                                            ",
6634"                                              ..................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..................                                              ",
6635"                                               ...................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...................                                               ",
6636"                                                 ....................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++....................                                                 ",
6637"                                                  .....................++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.....................                                                  ",
6638"                                                    ......................++++++++++++++++++++++++++++++++++++++++++++++++++++......................                                                    ",
6639"                                                      ........................++++++++++++++++++++++++++++++++++++++++++++........................                                                      ",
6640"                                                        ..........................++++++++++++++++++++++++++++++++++++..........................                                                        ",
6641"                                                         ..............................++++++++++++++++++++++++++..............................                                                         ",
6642"                                                            ................................................................................                                                            ",
6643"                                                              ............................................................................                                                              ",
6644"                                                                ........................................................................                                                                ",
6645"                                                                   ..................................................................                                                                   ",
6646"                                                                      ............................................................                                                                      ",
6647"                                                                        ........................................................                                                                        ",
6648"                                                                             ..............................................                                                                             ",
6649"                                                                                 ......................................                                                                                 ",
6650"                                                                                      ............................                                                                                      ",
6651"                                                                                                                                                                                                        ",
6652"                                                                                                                                                                                                        ",
6653"                                                                                                                                                                                                        ",
6654"                                                                                                                                                                                                        ",
6655"                                                                                                                                                                                                        ");
6656
6657
6658my $pixbuf_random = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@random);
6659# $pixbuf_sound->scale_simple ($dest_width, $dest_height, $interp_type)
6660my $random_w = 14; # (38 64) scale
6661my $random_h = 14;
6662$pixbuf_random = $pixbuf_random->scale_simple ( $random_w, $random_h, 'bilinear'); # hyper, bilinear
6663
6664my @random2 = @random; # To change the color of the random button, then make copy
6665$random2[3] = ".	c #BFBFBF"; # #BFBFBF
6666my $pixbuf_random2 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@random2);
6667$pixbuf_random2 = $pixbuf_random2->scale_simple ( $random_w, $random_h, 'bilinear');
6668
6669
6670my @loop = (
6671# /* columns rows colors chars-per-pixel */
6672"100 98 3 1",
6673" 	c None",
6674".	c $color_cdplayer_shadow",
6675"+	c $color_cdplayer_arrows",
6676"                                            ........................................                ",
6677"                                           ...........................................              ",
6678"                                           ............................................             ",
6679"                                    .      ...++++++++++++++++++++++++++++++++++++++...             ",
6680"                                .......    ...+++++++++++++++++++++++++++++++++++++....             ",
6681"                            ...........    ...++++++++++++++++++++++++++++++++++++....              ",
6682"                         .........++...    ...+++++++++++++++++++++++++++++++++++....               ",
6683"                       .......++++.....    ...++++++++++++++++++++++++++++++++++....                ",
6684"                     ......++++++.....     ...+++++++++++++++++++++++++++++++++....                 ",
6685"                   ......++++++.....       ...++++++++++++++++++++++++++++++++....                  ",
6686"                  .....+++++++....         ...+++++++++++++++++++++++++++++++....                   ",
6687"                .....++++++++....          ...++++++++++++++++++++++++++++++....                    ",
6688"               ....++++++++++...           ...++++++++++++++++++++++++++++.....                     ",
6689"              ....++++++++++...            ...+++++++++++++++++++++++++++.....                      ",
6690"             ....++++++++++...             ...++++++++++++++++++++++++++.....                       ",
6691"            ...+++++++++++....             ...+++++++++++++++++++++++++++.....                      ",
6692"           ...++++++++++++...              ...+++++++++++++++++++++++++++++.....                    ",
6693"          ...++++++++++++...               ...++++++++++++++++++++++++++++++.....                   ",
6694"         ....+++++++++++....               ...++++++++++++++++++++++++++++++++....                  ",
6695"         ...++++++++++++...                ...+++++++++++++++++++++++++++++++++....                 ",
6696"        ...++++++++++++...                 ...++++++++++++++++++++++++++++++++++....                ",
6697"       ...+++++++++++++...                 ...+++++++++++++++++++++++++++++++++++....               ",
6698"       ...++++++++++++...                  ...++++++++++++++++++++++++++++++++++++....              ",
6699"      ...+++++++++++++...                  ...+++++++++++++++++++++++++++++++++++++....             ",
6700"      ...+++++++++++++...                  ...++++++++++++++++++++++++++++++++++++++...             ",
6701"     ...+++++++++++++...                   ...+++++++++++++++++++++++++++++++++++++++...            ",
6702"     ...+++++++++++++...                   ...+++++++++++++++++++++++++++++++++++++++....           ",
6703"    ...++++++++++++++..                    ...++++++++++++++++++++++++++++++++++++++++....          ",
6704"    ...+++++++++++++...                    ...+++++++++++++++++++++++++++++++++++++++++...          ",
6705"    ..++++++++++++++...                    ...+++++++++++.+++++++++++++++++++++++++++++....         ",
6706"   ...++++++++++++++...                    ...++++++++++...+++++++++++++++++++++++++++++...         ",
6707"   ...++++++++++++++..                     ...++++++++++.....++++++++++++++++++++++++++++...        ",
6708"   ..+++++++++++++++..                     ...+++++++++.......+++++++++++++++++++++++++++...        ",
6709"  ...++++++++++++++...                     ...++++++++.... .....++++++++++++++++++++++++++...       ",
6710"  ...++++++++++++++...                     ...+++++++....   .....+++++++++++++++++++++++++...       ",
6711"  ..+++++++++++++++...                     ...++++++....      ....+++++++++++++++++++++++++...      ",
6712"  ..+++++++++++++++...                     ...+++++....        ....++++++++++++++++++++++++...      ",
6713" ...+++++++++++++++...                     ...+++++...          ....+++++++++++++++++++++++...      ",
6714" ...++++++++++++++++..                     ...++++...            ....+++++++++++++++++++++++...     ",
6715" ...++++++++++++++++..                     ...+++....             ....++++++++++++++++++++++...     ",
6716" ..+++++++++++++++++..                     ...++....               ....+++++++++++++++++++++...     ",
6717" ..+++++++++++++++++...                    ...+....                 ...++++++++++++++++++++++...    ",
6718" ..+++++++++++++++++...                    .......                   ...+++++++++++++++++++++...    ",
6719" ..+++++++++++++++++...                    ......                    ....++++++++++++++++++++...    ",
6720" ..++++++++++++++++++..                     ....                      ...++++++++++++++++++++...    ",
6721"...++++++++++++++++++...                     ..                        ...+++++++++++++++++++...    ",
6722"...++++++++++++++++++...                                               ...+++++++++++++++++++...    ",
6723"...++++++++++++++++++...                                                ...+++++++++++++++++++..    ",
6724" ..+++++++++++++++++++...                                               ...+++++++++++++++++++...   ",
6725" ..+++++++++++++++++++...                                               ...+++++++++++++++++++...   ",
6726" ..++++++++++++++++++++...                                               ...++++++++++++++++++...   ",
6727" ..++++++++++++++++++++...                        ..                     ...++++++++++++++++++...   ",
6728" ...++++++++++++++++++++...                     .....                    ...++++++++++++++++++...   ",
6729" ...++++++++++++++++++++...                     .....                     ...+++++++++++++++++..    ",
6730" ...+++++++++++++++++++++...                   ...+..                     ...++++++++++++++++...    ",
6731"  ..+++++++++++++++++++++....                 ...++..                     ...++++++++++++++++...    ",
6732"  ...+++++++++++++++++++++....               ...+++..                     ...++++++++++++++++...    ",
6733"  ...++++++++++++++++++++++...              ....+++..                     ...++++++++++++++++...    ",
6734"  ...+++++++++++++++++++++++...            ....++++..                     ...++++++++++++++++...    ",
6735"   ...+++++++++++++++++++++++...          ....+++++..                     ...++++++++++++++++...    ",
6736"   ...++++++++++++++++++++++++...        ....++++++..                      ..+++++++++++++++...     ",
6737"    ..+++++++++++++++++++++++++....     ....+++++++..                      ..+++++++++++++++...     ",
6738"    ...+++++++++++++++++++++++++....    ...++++++++..                     ...+++++++++++++++...     ",
6739"    ...++++++++++++++++++++++++++..... ...+++++++++...                    ...+++++++++++++++...     ",
6740"     ...++++++++++++++++++++++++++.......++++++++++...                    ...++++++++++++++...      ",
6741"     ...++++++++++++++++++++++++++++....+++++++++++...                    ...++++++++++++++...      ",
6742"      ...+++++++++++++++++++++++++++++..+++++++++++...                    ...++++++++++++++...      ",
6743"       ...+++++++++++++++++++++++++++++++++++++++++...                    ..++++++++++++++...       ",
6744"       ...+++++++++++++++++++++++++++++++++++++++++...                   ...++++++++++++++...       ",
6745"        ...++++++++++++++++++++++++++++++++++++++++...                   ...++++++++++++++...       ",
6746"         ...+++++++++++++++++++++++++++++++++++++++...                   ...+++++++++++++...        ",
6747"         ....++++++++++++++++++++++++++++++++++++++...                  ...++++++++++++++...        ",
6748"          ....+++++++++++++++++++++++++++++++++++++...                  ...+++++++++++++...         ",
6749"           ....++++++++++++++++++++++++++++++++++++...                 ...++++++++++++++...         ",
6750"            ....+++++++++++++++++++++++++++++++++++...                 ...+++++++++++++...          ",
6751"             ....++++++++++++++++++++++++++++++++++...                ...+++++++++++++....          ",
6752"              ....+++++++++++++++++++++++++++++++++...                ...+++++++++++++...           ",
6753"               ....++++++++++++++++++++++++++++++++...               ...+++++++++++++...            ",
6754"                .....++++++++++++++++++++++++++++++...               ...++++++++++++....            ",
6755"                 .....+++++++++++++++++++++++++++++...              ...++++++++++++....             ",
6756"                   .....+++++++++++++++++++++++++++...             ....+++++++++++....              ",
6757"                    .....++++++++++++++++++++++++++...            ....+++++++++++....               ",
6758"                    ....+++++++++++++++++++++++++++...           ....+++++++++++....                ",
6759"                   ....++++++++++++++++++++++++++++...           ...+++++++++++....                 ",
6760"                  ....+++++++++++++++++++++++++++++...         ....++++++++++.....                  ",
6761"                 ....++++++++++++++++++++++++++++++...        ....++++++++++.....                   ",
6762"                ....+++++++++++++++++++++++++++++++...       ....+++++++++.....                     ",
6763"               ....++++++++++++++++++++++++++++++++...     .....++++++++......                      ",
6764"              ....+++++++++++++++++++++++++++++++++...    .....++++++.......                        ",
6765"             ....++++++++++++++++++++++++++++++++++...   .....+++.........                          ",
6766"            ....+++++++++++++++++++++++++++++++++++...   ...............                            ",
6767"           ....++++++++++++++++++++++++++++++++++++...   ...........                                ",
6768"          ....+++++++++++++++++++++++++++++++++++++...    ......                                    ",
6769"          ...++++++++++++++++++++++++++++++++++++++...                                              ",
6770"          ............................................                                              ",
6771"           ..........................................                                               ",
6772"             ......                                                                                 ",
6773"                                                                                                    ");
6774
6775my $pixbuf_loop = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@loop);
6776# $pixbuf_sound->scale_simple ($dest_width, $dest_height, $interp_type)
6777my ( $loop_w , $loop_h ) = ( 15, 15 ); # Scale (60, 50)
6778$pixbuf_loop = $pixbuf_loop->scale_simple ( $loop_w, $loop_h, 'bilinear'); # hyper, bilinear
6779
6780my @loop2 = @loop; # To change the color of the loop button, then make copy
6781$loop2[3] = ".	c #BFBFBF"; # #A7B1BE
6782my $pixbuf_loop2 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@loop2);
6783$pixbuf_loop2 = $pixbuf_loop2->scale_simple ( $loop_w, $loop_h, 'bilinear');
6784
6785
6786my @buttons = (  # 8 buttons ; (x,y) = (256,128) = (4,2)*64 ; the dimension of each button is 64x64
6787# /* columns rows colors chars-per-pixel */
6788"256 128 3 1",
6789#" 	c #000000",
6790" 	c None",
6791".	c $color_cdplayer_arrows", # RGB = (  0,  0,  0) = black
6792#".	c #FFFFFF", # RGB = (255,255,255) = white
6793#"&	c #6e95ef", # azul m�dio
6794"&	c $color_cdplayer_shadow",
6795"                                                                                                                                                                                                                                                                ",
6796"                                                                                                                                                                                                                                                                ",
6797"                        &&&&&&&&&&&&&&&&                                                    &&&&&&&&                                                    &&&&&&&&&&&&&&&&                                                    &&&&&&&&                            ",
6798"                     &&&&&&&&&&&&&&&&&&&&&&                                            &&&&&&&&&&&&&&&&&&                                            &&&&&&&&&&&&&&&&&&&&&&                                            &&&&&&&&&&&&&&&&&&                       ",
6799"                   &&&&&&&&&&&&&&&&&&&&&&&&&&                                        &&&&&&&&&&&&&&&&&&&&&&                                        &&&&&&&&&&&&&&&&&&&&&&&&&&                                        &&&&&&&&&&&&&&&&&&&&&&                     ",
6800"                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&                  ",
6801"               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 ",
6802"              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               ",
6803"             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              ",
6804"           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&            ",
6805"          &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                     &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                     &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                     &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&           ",
6806"         &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&          ",
6807"         &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         ",
6808"        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         ",
6809"       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&        ",
6810"      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       ",
6811"      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&............&&&&&&&&............&&&&&&&&&             &&&&&&&&&&&&..&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&.&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       ",
6812"     &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&           &&&&&&&&&&............&&&&&&&&............&&&&&&&&&&           &&&&&&&&&&&&&...&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&           &&&&&&&&&&...&&&&&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&      ",
6813"     &&&&&&&&&&&&&............................&&&&&&&&&&&&&          &&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&          &&&&&&&&&&&&&.....&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&....&&&&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&     ",
6814"    &&&&&&&&&&&&&&............................&&&&&&&&&&&&&&         &&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&         &&&&&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         &&&&&&&&&&&......&&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&     ",
6815"    &&&&&&&&&&&&&&............................&&&&&&&&&&&&&&         &&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&         &&&&&&&&&&&&&&.........&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         &&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&     ",
6816"   &&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&       &&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&       &&&&&&&&&&&&&&&............&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       &&&&&&&&&&&&.........&&&&&&&&&&&&&&&&.......&&&&&&&&&&&&    ",
6817"   &&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&       &&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&       &&&&&&&&&&&&&&&..............&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       &&&&&&&&&&&&..........&&&&&&&&&&&&&&&.......&&&&&&&&&&&&    ",
6818"   &&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&      &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&      &&&&&&&&&&&&&&&................&&&&&&&&&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&............&&&&&&&&&&&&&.......&&&&&&&&&&&&&   ",
6819"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&     &&&&&&&&&&&&&&&&..................&&&&&&&&&&&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&.............&&&&&&&&&&&&.......&&&&&&&&&&&&&   ",
6820"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&     &&&&&&&&&&&&&&&&....................&&&&&&&&&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&...............&&&&&&&&&&.......&&&&&&&&&&&&&   ",
6821"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&     &&&&&&&&&&&&&&&&......................&&&&&&&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&................&&&&&&&&&.......&&&&&&&&&&&&&   ",
6822"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&     &&&&&&&&&&&&&&&&........................&&&&&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&..................&&&&&&&.......&&&&&&&&&&&&&   ",
6823"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&&    &&&&&&&&&&&&&&&&..........................&&&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&...................&&&&&&.......&&&&&&&&&&&&&&  ",
6824"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&&    &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.....................&&&&.......&&&&&&&&&&&&&&  ",
6825"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&&    &&&&&&&&&&&&&&&&..............................&&&&&&&&&&&&&&    &&&&&&&&&&&&&&......................&&&.......&&&&&&&&&&&&&&  ",
6826"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&&    &&&&&&&&&&&&&&&&................................&&&&&&&&&&&&    &&&&&&&&&&&&&&........................&.......&&&&&&&&&&&&&&  ",
6827"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&&    &&&&&&&&&&&&&&&&................................&&&&&&&&&&&&    &&&&&&&&&&&&&&........................&.......&&&&&&&&&&&&&&  ",
6828"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&&    &&&&&&&&&&&&&&&&..............................&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......................&&.......&&&&&&&&&&&&&&  ",
6829"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&&    &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.....................&&&&.......&&&&&&&&&&&&&&  ",
6830"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&&    &&&&&&&&&&&&&&&&..........................&&&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&....................&&&&&.......&&&&&&&&&&&&&&  ",
6831"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&     &&&&&&&&&&&&&&&&........................&&&&&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&..................&&&&&&&.......&&&&&&&&&&&&&   ",
6832"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&     &&&&&&&&&&&&&&&&......................&&&&&&&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&.................&&&&&&&&.......&&&&&&&&&&&&&   ",
6833"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&     &&&&&&&&&&&&&&&&....................&&&&&&&&&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&...............&&&&&&&&&&.......&&&&&&&&&&&&&   ",
6834"  &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&     &&&&&&&&&&&&&&&&..................&&&&&&&&&&&&&&&&&&&&&&&&&&     &&&&&&&&&&&&&..............&&&&&&&&&&&.......&&&&&&&&&&&&&   ",
6835"   &&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&      &&&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&&      &&&&&&&&&&&&&&&................&&&&&&&&&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&............&&&&&&&&&&&&&.......&&&&&&&&&&&&&   ",
6836"   &&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&       &&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&       &&&&&&&&&&&&&&&..............&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       &&&&&&&&&&&&...........&&&&&&&&&&&&&&.......&&&&&&&&&&&&    ",
6837"   &&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&       &&&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&&       &&&&&&&&&&&&&&&............&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       &&&&&&&&&&&&.........&&&&&&&&&&&&&&&&.......&&&&&&&&&&&&    ",
6838"    &&&&&&&&&&&&&&............................&&&&&&&&&&&&&&         &&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&         &&&&&&&&&&&&&&..........&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         &&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&     ",
6839"    &&&&&&&&&&&&&&............................&&&&&&&&&&&&&&         &&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&         &&&&&&&&&&&&&&........&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         &&&&&&&&&&&......&&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&     ",
6840"     &&&&&&&&&&&&&............................&&&&&&&&&&&&&          &&&&&&&&&&&............&&&&&&&&............&&&&&&&&&&&          &&&&&&&&&&&&&.....&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&....&&&&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&     ",
6841"     &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&           &&&&&&&&&&............&&&&&&&&............&&&&&&&&&&           &&&&&&&&&&&&&...&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&           &&&&&&&&&&...&&&&&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&      ",
6842"      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&............&&&&&&&&............&&&&&&&&&             &&&&&&&&&&&&..&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&.&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       ",
6843"      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       ",
6844"       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&        ",
6845"        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         ",
6846"         &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         ",
6847"         &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&          ",
6848"          &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                     &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                     &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                     &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&           ",
6849"           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&            ",
6850"             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              ",
6851"              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                             &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               ",
6852"               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 ",
6853"                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&                  ",
6854"                   &&&&&&&&&&&&&&&&&&&&&&&&&&                                        &&&&&&&&&&&&&&&&&&&&&&                                        &&&&&&&&&&&&&&&&&&&&&&&&&&                                        &&&&&&&&&&&&&&&&&&&&&&                     ",
6855"                     &&&&&&&&&&&&&&&&&&&&&&                                            &&&&&&&&&&&&&&&&&&                                            &&&&&&&&&&&&&&&&&&&&&&                                            &&&&&&&&&&&&&&&&&&                       ",
6856"                        &&&&&&&&&&&&&&&&                                                    &&&&&&&&                                                    &&&&&&&&&&&&&&&&                                                    &&&&&&&&                            ",
6857"                                                                                                                                                                                                                                                                ",
6858"                                                                                                                                                                                                                                                                ",
6859"                                                                                                                                                                                                                                                                ",
6860"                                                                                                                                                                                                                                                                ",
6861"                            &&&&&&&&                                                        &&&&&&&&                                                        &&&&&&&&                                                        &&&&&&&&                            ",
6862"                       &&&&&&&&&&&&&&&&&&                                              &&&&&&&&&&&&&&&&&&                                              &&&&&&&&&&&&&&&&&&                                              &&&&&&&&&&&&&&&&&&                       ",
6863"                     &&&&&&&&&&&&&&&&&&&&&&                                          &&&&&&&&&&&&&&&&&&&&&&                                          &&&&&&&&&&&&&&&&&&&&&&                                          &&&&&&&&&&&&&&&&&&&&&&                     ",
6864"                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&                  ",
6865"                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 ",
6866"               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               ",
6867"              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              ",
6868"            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&            ",
6869"           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&           ",
6870"          &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&          ",
6871"         &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         ",
6872"         &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         ",
6873"        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                &&&&&&&&&&&&&&&&&&&&&&&..&&&&&&&&&&&&&&&&&&&&&&&                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&        ",
6874"       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&&....&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       ",
6875"       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&......&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&.&&&&&&&&&       ",
6876"      &&&&&&&&.&&&&&&&&&&&&&&&&&&.&&&&&&&&&&&&&&&&&&&&&&&&            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&            &&&&&&&&&&&&&&&&&&&&&&........&&&&&&&&&&&&&&&&&&&&&&            &&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&&&&...&&&&&&&&&&      ",
6877"     &&&&&&&&&...&&&&&&&&&&&&&&&&...&&&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&&&&&&.&&&&&&&&&&&&&&&&&&.&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&&&..........&&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&&&....&&&&&&&&&&&     ",
6878"     &&&&&&&&&....&&&&&&&&&&&&&&&.....&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&&&&...&&&&&&&&&&&&&&&&...&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&&............&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&......&&&&&&&&&&&     ",
6879"     &&&&&&&&&......&&&&&&&&&&&&&......&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&&.....&&&&&&&&&&&&&&&....&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&..............&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&     ",
6880"    &&&&&&&&&&.......&&&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&&        &&&&&&&&&&&&&&&&&&&&&......&&&&&&&&&&&&&......&&&&&&&&&&        &&&&&&&&&&&&&&&&&&&&................&&&&&&&&&&&&&&&&&&&&        &&&&&&&&&&&&.......&&&&&&&&&&&&&&&&.........&&&&&&&&&&&&    ",
6881"    &&&&&&&&&&.........&&&&&&&&&&.........&&&&&&&&&&&&&&&&&&        &&&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&&.......&&&&&&&&&&        &&&&&&&&&&&&&&&&&&&&................&&&&&&&&&&&&&&&&&&&&        &&&&&&&&&&&&.......&&&&&&&&&&&&&&&..........&&&&&&&&&&&&    ",
6882"   &&&&&&&&&&&...........&&&&&&&&...........&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&&.........&&&&&&&&&&.........&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&&&..................&&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&&&&&&&............&&&&&&&&&&&&&   ",
6883"   &&&&&&&&&&&............&&&&&&&............&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&&&&&...........&&&&&&&&...........&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&&....................&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&&&&&&.............&&&&&&&&&&&&&   ",
6884"   &&&&&&&&&&&..............&&&&&..............&&&&&&&&&&&&&&      &&&&&&&&&&&&&&&&............&&&&&&&............&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&......................&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&&&&...............&&&&&&&&&&&&&   ",
6885"   &&&&&&&&&&&...............&&&&...............&&&&&&&&&&&&&      &&&&&&&&&&&&&&..............&&&&&..............&&&&&&&&&&&      &&&&&&&&&&&&&&&&&........................&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&&&................&&&&&&&&&&&&&   ",
6886"   &&&&&&&&&&&.................&&.................&&&&&&&&&&&      &&&&&&&&&&&&&...............&&&&...............&&&&&&&&&&&      &&&&&&&&&&&&&&&&&........................&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&..................&&&&&&&&&&&&&   ",
6887"  &&&&&&&&&&&&..................&...................&&&&&&&&&&    &&&&&&&&&&&&.................&&.................&&&&&&&&&&&&    &&&&&&&&&&&&&&&&&..........................&&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......&&&&&&...................&&&&&&&&&&&&&&  ",
6888"  &&&&&&&&&&&&.......................................&&&&&&&&&    &&&&&&&&&&...................&..................&&&&&&&&&&&&    &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......&&&&.....................&&&&&&&&&&&&&&  ",
6889"  &&&&&&&&&&&&........................................&&&&&&&&    &&&&&&&&&.......................................&&&&&&&&&&&&    &&&&&&&&&&&&&&&&............................&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......&&&......................&&&&&&&&&&&&&&  ",
6890"  &&&&&&&&&&&&.........................................&&&&&&&    &&&&&&&&........................................&&&&&&&&&&&&    &&&&&&&&&&&&&&&..............................&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......&........................&&&&&&&&&&&&&&  ",
6891"  &&&&&&&&&&&&.........................................&&&&&&&    &&&&&&&.........................................&&&&&&&&&&&&    &&&&&&&&&&&&&&................................&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......&........................&&&&&&&&&&&&&&  ",
6892"  &&&&&&&&&&&&........................................&&&&&&&&    &&&&&&&.........................................&&&&&&&&&&&&    &&&&&&&&&&&&&&................................&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......&&.......................&&&&&&&&&&&&&&  ",
6893"  &&&&&&&&&&&&.......................................&&&&&&&&&    &&&&&&&&........................................&&&&&&&&&&&&    &&&&&&&&&&&&&..................................&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......&&&&.....................&&&&&&&&&&&&&&  ",
6894"  &&&&&&&&&&&&..................&..................&&&&&&&&&&&    &&&&&&&&&.......................................&&&&&&&&&&&&    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&    &&&&&&&&&&&&&&.......&&&&&....................&&&&&&&&&&&&&&  ",
6895"   &&&&&&&&&&&................&&&................&&&&&&&&&&&&      &&&&&&&&&&..................&..................&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&..................&&&&&&&&&&&&&   ",
6896"   &&&&&&&&&&&...............&&&&...............&&&&&&&&&&&&&      &&&&&&&&&&&&................&&&................&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&&.................&&&&&&&&&&&&&   ",
6897"   &&&&&&&&&&&.............&&&&&&.............&&&&&&&&&&&&&&&      &&&&&&&&&&&&&...............&&&&...............&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&&&&...............&&&&&&&&&&&&&   ",
6898"   &&&&&&&&&&&..........&&&&&&&&&..........&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&&&.............&&&&&&.............&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&&&&&..............&&&&&&&&&&&&&   ",
6899"   &&&&&&&&&&&.........&&&&&&&&&&.........&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&..........&&&&&&&&&..........&&&&&&&&&&&      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&      &&&&&&&&&&&&&.......&&&&&&&&&&&&&............&&&&&&&&&&&&&   ",
6900"    &&&&&&&&&&.......&&&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&&        &&&&&&&&&&&&&&&&&&.........&&&&&&&&&&.........&&&&&&&&&&        &&&&&&&&&&&&...............................&&&&&&&&&&&&&        &&&&&&&&&&&&.......&&&&&&&&&&&&&&...........&&&&&&&&&&&&    ",
6901"    &&&&&&&&&&.....&&&&&&&&&&&&&&......&&&&&&&&&&&&&&&&&&&&&        &&&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&&.......&&&&&&&&&&        &&&&&&&&&&&&...............................&&&&&&&&&&&&&        &&&&&&&&&&&&.......&&&&&&&&&&&&&&&&.........&&&&&&&&&&&&    ",
6902"     &&&&&&&&&....&&&&&&&&&&&&&&&....&&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&......&&&&&&&&&&&&&&.....&&&&&&&&&          &&&&&&&&&&&...............................&&&&&&&&&&&&          &&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&.......&&&&&&&&&&&     ",
6903"     &&&&&&&&&...&&&&&&&&&&&&&&&&...&&&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&&&....&&&&&&&&&&&&&&&....&&&&&&&&&          &&&&&&&&&&&...............................&&&&&&&&&&&&          &&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&......&&&&&&&&&&&     ",
6904"     &&&&&&&&&.&&&&&&&&&&&&&&&&&&.&&&&&&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&&&&&&&&&&&&&...&&&&&&&&&&&&&&&&...&&&&&&&&&          &&&&&&&&&&&...............................&&&&&&&&&&&&          &&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&&&....&&&&&&&&&&&     ",
6905"      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&            &&&&&&&&&&&&&&&&&&&&&&&&.&&&&&&&&&&&&&&&&&&.&&&&&&&&            &&&&&&&&&&...............................&&&&&&&&&&&            &&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&&&&...&&&&&&&&&&      ",
6906"       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&...............................&&&&&&&&&&              &&&&&&&&&.......&&&&&&&&&&&&&&&&&&&&&&&&.&&&&&&&&&       ",
6907"       &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              &&&&&&&&&...............................&&&&&&&&&&              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       ",
6908"        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                &&&&&&&&...............................&&&&&&&&&                &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&        ",
6909"         &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         ",
6910"         &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&         ",
6911"          &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&          ",
6912"           &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&           ",
6913"            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&            ",
6914"              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&              ",
6915"               &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                              &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&               ",
6916"                 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&                 ",
6917"                  &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                    &&&&&&&&&&&&&&&&&&&&&&&&&&&&                  ",
6918"                     &&&&&&&&&&&&&&&&&&&&&&                                          &&&&&&&&&&&&&&&&&&&&&&                                          &&&&&&&&&&&&&&&&&&&&&&                                          &&&&&&&&&&&&&&&&&&&&&&                     ",
6919"                       &&&&&&&&&&&&&&&&&&                                              &&&&&&&&&&&&&&&&&&                                              &&&&&&&&&&&&&&&&&&                                              &&&&&&&&&&&&&&&&&&                       ",
6920"                            &&&&&&&&                                                        &&&&&&&&                                                        &&&&&&&&                                                        &&&&&&&&                            ",
6921"                                                                                                                                                                                                                                                                ",
6922"                                                                                                                                                                                                                                                                ");
6923
6924
6925
6926my $pixbuf_buttons = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@buttons);
6927my ( $buttons_w, $buttons_h ) = ( 15 , 15 );
6928my ( $buttons_ws, $buttons_hs ) = ( 4*$buttons_w , 2*$buttons_h ); # (256,128) = (4,2)*64
6929$pixbuf_buttons = $pixbuf_buttons->scale_simple ( $buttons_ws, $buttons_hs, 'bilinear'); # hyper, bilinear
6930
6931# To change the color of buttons (shadow) - Motion
6932my @buttons2 = @buttons; # make copy
6933$buttons2[2] = ".	c $color_cdplayer_arrows_motion";
6934$buttons2[3] = "&	c $color_cdplayer_shadow_motion";
6935#"&	c #FFFFFF"; # white color
6936my $pixbuf_buttons2 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@buttons2);
6937$pixbuf_buttons2 = $pixbuf_buttons2->scale_simple ( $buttons_ws, $buttons_hs, 'bilinear'); # hyper, bilinear
6938
6939# To change the color of buttons (arrows), - Pressed
6940my @buttons3 = @buttons; # make copy
6941$buttons3[2] = ".	c #000000";
6942#"&	c #FFFFFF"; # white color
6943my $pixbuf_buttons3 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@buttons3);
6944$pixbuf_buttons3 = $pixbuf_buttons3->scale_simple ( $buttons_ws, $buttons_hs, 'bilinear'); # hyper, bilinear
6945
6946my @posbar = ( # position bar of cd player
6947# /* columns rows colors chars-per-pixel */
6948"30 10 16 1",
6949" 	c None",
6950".	c #2451BA",
6951"+	c #2C61C2",
6952"x	c #2E6BC5",
6953"#	c #487AD0",
6954"S	c #4081D3",
6955"%	c #4C8FDE",
6956"&	c #598ECF",
6957"*	c #589DEB",
6958"=	c #64A1E4",
6959"-	c #72ADEC",
6960";	c #6AB0F8",
6961">	c #83B1E5",
6962",	c #7BC3FF",
6963"'	c #89CFFF",
6964")	c #95D9FF",
6965"   .....++++++++++++++.....   ",
6966"  +++x#######SSSS#####xxx++.  ",
6967" ..S=>>>>>>>>>>>>>>>>>>>-&#.. ",
6968"..x%=>>>>>>>>>>>>>>>>>>>-=%+.+",
6969"+x#%**=****===--====*****%Sx++",
6970"x#S%********=;---;;=****%%S#xx",
6971"xS**;;;;;;;;;,,,,,;;;;;;***%S#",
6972" S*;;,,,,,,,'')))'',,,,,;;=&S ",
6973"  &-,,'''''')))))))))'',,-=&  ",
6974"   x##########SS#########xx   ");
6975
6976my $pixbuf_posbar = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@posbar);
6977my ( $posbar_w, $posbar_h ) = ( 12 , 5 ); # (30,10) = (3,1)*10
6978$pixbuf_posbar = $pixbuf_posbar->scale_simple ( $posbar_w, $posbar_h, 'bilinear'); # hyper, bilinear
6979
6980
6981my @numbers2 = ( # min:sec of cd player
6982# /* columns rows colors chars-per-pixel */
6983"420 40 3 1",
6984" 	c None",
6985".	c $color_cdplayer_digit",
6986"+	c None",    # none, background color
6987#"+	c #ADD8E6",
6988"     ....................          ++++++++++++++++++++          ....................          ....................          ++++++++++++++++++++          ....................          ....................          ....................          ....................          ....................                             ...                                                                    ++++++++++++++++++++     ",
6989"     ....................          ++++++++++++++++++++          ....................          ....................          ++++++++++++++++++++          ....................          ....................          ....................          ....................          ....................                            ....                                                                    ++++++++++++++++++++     ",
6990"   ..  ................  ..      ++  ++++++++++++++++  ..      ++  ................  ..      ++  ................  ..      ..  ++++++++++++++++  ..      ..  ................  ++      ..  ................  ++      ++  ................  ..      ..  ................  ..      ..  ................  ..                         .....                                                                  ++  ++++++++++++++++  ++   ",
6991"   ..  ................  ..      ++  ++++++++++++++++  ..      ++  ................  ..      ++  ................  ..      ..  ++++++++++++++++  ..      ..  ................  ++      ..  ................  ++      ++  ................  ..      ..  ................  ..      ..  ................  ..                         ....                                                                   ++  ++++++++++++++++  ++   ",
6992"   .... .............. ....      ++++                ....      ++++ .............. ....      ++++ .............. ....      ....                ....      .... .............. ++++      .... .............. ++++      ++++ .............. ....      .... .............. ....      .... .............. ....                        ....                                                                    ++++                ++++   ",
6993"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                        ...                                                                     ++++                ++++   ",
6994"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                       ....                                                                     ++++                ++++   ",
6995"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                      ....                    ......                                            ++++                ++++   ",
6996"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                      ....                    ......                                            ++++                ++++   ",
6997"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                     ....                     ......                                            ++++                ++++   ",
6998"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                     ....                     ......                                            ++++                ++++   ",
6999"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                    ....                      ......                                            ++++                ++++   ",
7000"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                    ....                      ......                                            ++++                ++++   ",
7001"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                   ....                                                                         ++++                ++++   ",
7002"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                   ....                                                                         ++++                ++++   ",
7003"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                  ....                                                                          ++++                ++++   ",
7004"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                  ....                                                                          ++++                ++++   ",
7005"   .....              .....      ++++               .....      ++++               .....      ++++               .....      .....              .....      .....               ++++      .....               ++++      ++++               .....      .....              .....      .....              .....                 ....                                                                           ++++                ++++   ",
7006"   ..  ++++++++++++++++  ..      ++  ++++++++++++++++  ..      ++  ................  ..      ++  ................  ..      ..  ................  ..      ..  ................  ++      ..  ................  ++      ++  ++++++++++++++++  ..      ..  ................  ..      ..  ................  ..                 ....                                                ..................         ++  ++++++++++++++++  ++   ",
7007"   ..  ++++++++++++++++  ..      ++  ++++++++++++++++  ..      ++  ................  ..      ++  ................  ..      ..  ................  ..      ..  ................  ++      ..  ................  ++      ++  ++++++++++++++++  ..      ..  ................  ..      ..  ................  ..                ....                                                 ..................         ++  ++++++++++++++++  ++   ",
7008"   ..  ++++++++++++++++  ..      ++  ++++++++++++++++  ..      ..  ................  ++      ++  ................  ..      ++  ................  ..      ++  ................  ..      ..  ................  ..      ++  ++++++++++++++++  ..      ..  ................  ..      ++  ................  ..                ....                                                 ..................         ++  ++++++++++++++++  ++   ",
7009"   ..  ++++++++++++++++  ..      ++  ++++++++++++++++  ..      ..  ................  ++      ++  ................  ..      ++  ................  ..      ++  ................  ..      ..  ................  ..      ++  ++++++++++++++++  ..      ..  ................  ..      ++  ................  ..               ....                                                  ..................         ++  ++++++++++++++++  ++   ",
7010"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....               ....                                                  ..................         ++++                ++++   ",
7011"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....              ....                                                                              ++++                ++++   ",
7012"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....              ....                                                                              ++++                ++++   ",
7013"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....             ....                             ......                                            ++++                ++++   ",
7014"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....             ....                             ......                                            ++++                ++++   ",
7015"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....            ....                              ......                                            ++++                ++++   ",
7016"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....            ....                              ......                                            ++++                ++++   ",
7017"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....           ....                               ......                                            ++++                ++++   ",
7018"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....           ....                               ......                                            ++++                ++++   ",
7019"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....          ....                                                                                  ++++                ++++   ",
7020"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....          ....                                                                                  ++++                ++++   ",
7021"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....         ....                                                                                   ++++                ++++   ",
7022"   .....              .....      ++++               .....      .....               ++++      ++++               .....      ++++               .....      ++++               .....      .....              .....      ++++               .....      .....              .....      ++++               .....         ....                                                                                   ++++                ++++   ",
7023"   .... .............. ....      ++++                ....      .... .............. ++++      ++++ .............. ....      ++++                ....      ++++ .............. ....      .... .............. ....      ++++                ....      .... .............. ....      ++++ .............. ....        ....                                                                                    ++++                ++++   ",
7024"   ..  ................  ..      ++  ++++++++++++++++  ..      ..  ................  ++      ++  ................  ..      ++  ++++++++++++++++  ..      ++  ................  ..      ..  ................  ..      ++  ++++++++++++++++  ..      ..  ................  ..      ++  ................  ..        ....                                                                                    ++  ++++++++++++++++  ++   ",
7025"   ..  ................  ..      ++  ++++++++++++++++  ..      ..  ................  ++      ++  ................  ..      ++  ++++++++++++++++  ..      ++  ................  ..      ..  ................  ..      ++  ++++++++++++++++  ..      ..  ................  ..      ++  ................  ..       ....                                                                                     ++  ++++++++++++++++  ++   ",
7026"     ....................          ++++++++++++++++++++          ....................          ....................          ++++++++++++++++++++          ....................          ....................          ++++++++++++++++++++          ....................          ....................         ....                                                                                       ++++++++++++++++++++     ",
7027"     ....................          ++++++++++++++++++++          ....................          ....................          ++++++++++++++++++++          ....................          ....................          ++++++++++++++++++++          ....................          ....................        ....                                                                                        ++++++++++++++++++++     ");
7028
7029
7030my $pixbuf_numbers = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@numbers2);
7031my ( $numbers_w, $numbers_h ) = ( 9 , 12 ); # (30,40) = 10 * (3,4) = (width,height) of the the 14 digits
7032my ( $numbers_ws, $numbers_hs ) = ( 14*$numbers_w , 1*$numbers_h ); # (420,40) = (14*30,1*40)
7033$pixbuf_numbers = $pixbuf_numbers->scale_simple ( $numbers_ws, $numbers_hs, 'bilinear'); # hyper, bilinear
7034
7035# To change the color of numbers on the display - lapsed time color
7036my @numbers3 = @numbers2; # make copy and change only the color information
7037$numbers3[2] = ".	c $color_cdplayer_digit_remaining"; #red , color information
7038
7039my $pixbuf_numbers2 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@numbers3);
7040$pixbuf_numbers2 = $pixbuf_numbers2->scale_simple ( $numbers_ws, $numbers_hs, 'bilinear'); # hyper, bilinear
7041
7042
7043my @skin_xTunes = ( # cd player skin_xTunes
7044# /* columns rows colors chars-per-pixel */
7045"225 90 80 1",
7046".	c none",
7047" 	c #484342",
7048"+	c #595553",
7049"Q	c #636562",
7050"#	c #666B6D",
7051"P	c #6A6B69",
7052"%	c #6E6D65",
7053"S	c #747672",
7054"*	c #7D7E78",
7055"=	c #828677",
7056"-	c #848683",
7057";	c #8C8D8A",
7058">	c #8F9283",
7059",	c #939491",
7060"R	c #989B8C",
7061")	c #989B97",
7062"!	c #A0A29F",
7063"~	c #A1A495",
7064"{	c #A6A1A0",
7065"]	c #9FA4A6",
7066"^	c #A4A2A6",
7067"/	c #A0A69C",
7068"(	c #A3A5A2",
7069"_	c #A7AA94",
7070":	c #A6A8A5",
7071"<	c #ACA7A6",
7072"[	c #A9ABA2",
7073"}	c #A6ABAE",
7074"|	c #ABA9AD",
7075"1	c #A9ABA8",
7076"2	c #ABADAA",
7077"3	c #B1ABAA",
7078"4	c #ACB0A0",
7079"5	c #AFB1AE",
7080"6	c #B2B1A9",
7081"7	c #ADB3B5",
7082"8	c #B6B0AF",
7083"9	c #B3B1B5",
7084"0	c #B2B4B1",
7085"a	c #B6B8B5",
7086"b	c #BDB8B6",
7087"c	c #B9BCAC",
7088"d	c #BBB8BD",
7089"e	c #B8C0A8",
7090"f	c #BABCB9",
7091"g	c #B7BDBF",
7092"h	c #BBC0C3",
7093"i	c #BEC0BD",
7094"j	c #BEC2B8",
7095"k	c #BDC5AE",
7096"l	c #C0C4B4",
7097"m	c #C3C0C5",
7098"n	c #C6C0BF",
7099"o	c #BEC3C6",
7100"p	c #C2C4C1",
7101"q	c #C6C9B9",
7102"r	c #C4CBB4",
7103"s	c #CBC5C4",
7104"t	c #C8C6CA",
7105"u	c #C6C8C5",
7106"v	c #C7C9C6",
7107"w	c #C8CCBB",
7108"x	c #C9CBC8",
7109"y	c #CEC9C9",
7110"z	c #CACCC9",
7111"A	c #CBD1B7",
7112"B	c #CCCECB",
7113"C	c #CED0CC",
7114"D	c #CFD1CE",
7115"E	c #D0D2CF",
7116"F	c #D1D9C1",
7117"G	c #D4DABC",
7118"H	c #DADCD8",
7119"I	c #DFDAD8",
7120"J	c #DBE1C2",
7121"K	c #DFE1DE",
7122"L	c #E4E3DA",
7123"M	c #E5E4DB",
7124"N	c #E6E9E5",
7125"O	c #EDF0EC",
7126".....NNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOON.....",
7127"...HHHCuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvpBHHH...",
7128"..CEu35780aaffniiptttyzBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBxytttpiindfaa0812apCB..",
7129".ipa|<11|3500abfbfimptuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzvvtumpinfbaaa089223|aif.",
7130".aa:<:1125500aafffmipptvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzBBBBBBxtuppmifffaaa0052211:aa.",
7131":2!{(]:||225508aaffiiopuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuupoiifffab00522||:]:{!:(",
7132"!::1322500aaafffipppuvxzBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBxvuuppiifffaa05552131::(",
7133",(^::11129500ddfffiipsutyvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzBBBBBBBBytvspopiifddaa09522111^:;",
7134"((]::1127500aadffihnptuvvvvvvvBBBBzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvvvvvvvvvutpnhiffdaa0057211::]((,",
7135"11312550aaafffiippuvxztCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCtzxvuppiifffaaa05521311)",
7136"(((::11|5509adfffinoputtvvvvvvBBBBBBBBzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvvvvvvvvttuponifffda9055|11::(((,",
7137"::|}33508aaaffiipptuvxzDBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBDzxvutppiiffaaa80533}|::)",
7138"(]:|1122900abdffifa!;SSSPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPSSS;!afiffdba0092211|:](,",
7139"((:}1225580aaaa:;SQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQS;:aaaa0855221}:((,",
7140"!^]<:11238800:;SQ%*=>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>=*%QS;:00883211:<]^!,",
7141"(]::11225550,SP%=R_ekkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkke_R=%PS,05552211::](,",
7142":^|3}225002;PP=_erGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGre_=PP;200522}3|^:)",
7143":112255501-PSReAGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGAeRSP-105552211:)",
7144"(({::118:-P*_kGGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGGk_*P-:811::{((,",
7145":1112252;S*_AGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGA_*S;2522111:)",
7146":::}11a,S*_rGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGr_*S,a11}:::)",
7147"!](<:a:-*~rFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFr~*-:a:<(]!,",
7148"!!(^5a)*RkGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGkR*)a5^(!!,",
7149"((:(f1;-eFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFe-;1f(:((,",
7150"::12a!-_rGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGr_-!a21::)",
7151"!!]b0)-eGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGe-)0b]!!,",
7152"({!f1,RrJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJrR,1f!{(,",
7153"::1f1,_GJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJG_,1f1::)",
7154"!)5f1,cGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGc,1f5)!,",
7155":!ff1ReGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGeR1ff!:)",
7156":)fa1/kJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJk/1af):)",
7157":!ii5/rJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJr/5ii!:)",
7158":)ff5~rJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJr~5ff):)",
7159":!ipf[rJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJr[fpi!:)",
7160"!!aif[kJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJk[fia!!,",
7161"(!5pi[cJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJc[ip5!(,",
7162"1(<pu0cGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGc0up<(1)",
7163"(({fpf4GJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJG4fpf{((,",
7164"(()fpv6rJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJr6vpf)((,",
7165"!]!3uBpcGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGcpBu3!]!,",
7166"!!(:ivB6rJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJr6Bvi:(!!,",
7167"!^(!2vCvcGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGcvCv2!(^!,",
7168":111{fvCjkGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGkjCvf{111:)",
7169"!((:{1pCCjrGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGrjCCp1{:((!,",
7170"!^((:(8xHElrJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJrlEHx8(:((^!,",
7171"::1221:0BHEjrGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGrjEHB0:1221::)",
7172"!!((::11aCHKjkGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGkjKHCa11::((!!,",
7173"1112955518zHKClrGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGrlCKHz8155592111)",
7174":11}3250925vHNKCqrAGGJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJGGArqCKNHv5290523}11:)",
7175"(({::1125558bEKNKHwwqeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeqwwHKNKEb8555211::{((,",
7176"!!]]^:|3225508avHNNKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMLLLLLLLLLLKKKNNHva8055223|:^]]!!,",
7177"111225509aaffff0afpEKNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNKEpfa0ffffaa905522111)",
7178"::122550aaafffnipppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppnnnpnppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppinfffaaa055221::)",
7179"!^(::}|235500aabffioopuyvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvyupooiffbaa005532|}::(^!,",
7180"::11322507aaagbhionsuvxDBBBBBBzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzBBBBBBBBDxvusnoihbgaaa70522311::)",
7181":::1129509aaafnihmpuvvxBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBxvvupmhinfaaa9059211:::)",
7182",((]}1122588adbbfiippoutxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBtvtsppiinbdab805521}}::)",
7183",((:|1233580aadffiiopuuvxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBzvvuopiifdfaa808322|:::)",
7184"):|1}2559aa,PPPPP##PPPPPPPPPPPPPPPPPP#PP#PPPPPPP##PP#PPP##PPPPPPPPPPPPPPPPPP#PP#PPPPPPP##PP#PPP#PP##PPPPPPPPPPPPPPPPPP#PP#PPPPPPP##PP#PPP#PP##PPPPPPPPPPPPPPPPPP#PP#PPPPPPP##PP#PPP#PP##PPPPPPPPPP#PP#PPPPPPP##PP#P#P,a00552}1|:)",
7185",!!^^::1112(({!{!(!!{{{!{{{({{!{!{{{!{{{{{{!{{{!!(!{!{!!(!!{{{!{{{({{!{!{{{!{{{{{{!{{{!!(!{!{!{!{!(!!{{{!{{{({{!{!{{{!{{{{{{!{{{!!(!{!{!{!{!(!!{{{!{{{({{!{!{{{!{{{{{{!{{{!!(!{!{!{!{!(!!{{{!{{{{!{{{{{{!{{{!!(!{!{!((2211::^^!!,",
7186"):11|}5508bpHIHIHHIHHIHHHHHHIHIIIHHHHHHHHHHHHIHHIHHIHHHHHIHHIHHHHHHIHIIIHHHHHHHHHHHHIHHIHHIHHHHHIHHIHHIHHHHHHIHIIIHHHHHHHHHHHHIHHIHHIHHHHHIHHIHHIHHHHHHIHIIIHHHHHHHHHHHHIHHIHHIHHHHHIHHIHHIHHHHHHHHHHHHHHHIHHIHHIHIHHpb8055}|11:)",
7187",!((^:113552500aaffiippppppppppppppppiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiippppppppppppppppuuuuuuuupppppuuuuuppppppppppppppppppppppppiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiipppppppppppppiffaa0052053221|:((,",
7188",!!{(:|11|57908aadgiipppvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvpppiigdaa80975|11|:({!!,",
7189"):11325509abdffiippstvxBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBxvtsppiiffdba90552311:)",
7190"):::122253500abfbfimptuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvvvvvvvvttupnnmgffaa0085211::(((,",
7191",!!^{<:115500aafffmipptvvvvvvvvvvvvvvvvvvvvvvvvvvvvvppppppppppppppppppppppppppppppppppppppppppppppppnnnpnpppppppppppppppppppppppppppppppuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuspmpinffaa78952}11:<{^!!,",
7192",!((:|112225508aaffiiopuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuppnihdfaaa9559211|:((!,",
7193")::1132250aaafffipppuvxzBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBDxvusnoihbgaaa70522311::)",
7194"):::112959500ddfffiipsutyvvvvvvvvvvvvvvvvvvvvvvvvvvvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBxvvupmhinfaaa9059211:::)",
7195",!!{(]:11500aadffihnptuvvvvvvvBBBBzzzzzzzzzzzzzzzzzzvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvpppiigdaa80975|11|:({!!,",
7196":|2255009500ddfffiipsutyvvvvvvvvvvvvvvvvvvvvvvvvvvvppppppppppppppppppppppppppppppppppppppppppppppppnnnpnppppppppppppppppppppppppppppppppppppppnnnpnppppppppppppppppppppppppnnnpnpppppppppppppppvvvvvvvvvvpppiigdaa80975|11|:({!!,",
7197"<1125590500aadffihnptuvvvvvvvBBBBzzzzzzzzzzzzzzzzzzvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvCCCCCCCCCBBxvtsppiiffdba90552311:)",
7198"!^::}12270aadgfiimppuvyxxxxxxvvvvvvvvvvvvvvvvvvvvvvzzzzzzzzzzxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxzzzzzzzzzzzzzzzzzzzzzzxxxxxxvvvvvvvvttupnnmgffaa0085211::(((,",
7199"^:}322553500abfbfimptuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBuuuuuuuuuspmpinffaa78952}11:<{^!!,",
7200"!(]:<1135500aafffmipptvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvuppnihdfaaa9559211|:((!,",
7201"(:::1129225508aaffiiopuuuuuuuuuuuuuuuuuuuuuuuuuuuuuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzCCBBBBBBBDxvusnoihbgaaa70522311::)",
7202"112}550a0aaafffipppuvxzBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCvvvvvvvvvvpppiigdaa80975|11|:({!!,",
7203"(^:111|59500ddfffiipsutyvvvvvvvvvvvvvvvvvvvvvvvvvvvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBxvtsppiiffdba90552311:)",
7204"]{(::1}}500aadffihnptuvvvvvvvBBBBzzzzzzzzzzzzzzzzzzvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvxvvvvvvvvttupnnmgffaa0085211::(((,",
7205"1}12250070aadgfiimppuvyxxxxxxvvvvvvvvvvvvvvvvvvvvvvzzzzzzzzzzxxxxxxxxxxxxxxxxxxxxxxzzzxxxxxxxzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxzzzzzzzzzzzzzzzxxxxxuuuuuuuuuspmpinffaa78952}11:<{^!!,",
7206"]112227500aafffioppuvtzzzzzzzvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvuppnihdfaaa9559211|:((!,",
7207":}}12550550adagffinpputvvvvvvvvvvvvvvvvvvvvvvvvvvvvzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzCCBBBBBBBDxvusnoihbgaaa70522311::)",
7208"-!!](::1aafbhhppsuvvzBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCuuuuuuuspppiiffaba7552|11::(]!!-",
7209"-,((^:1|32550ababpuuvxyBBBBBBBBBBBBBBBBBBBBBBBBzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvtuppiiffbaba05523|1:^((,-",
7210"*;:|13125580adfffinppttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzzzzzztvvupniifffda08552131|:;*",
7211".S;1235500aadffhituvvyBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDBzyyupmihffdaa0055321;S.",
7212".QP;22557aaaffmhipsvvzBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCyxvystpihmffaaa75522;PQ.",
7213"..++*,!(700aadffimipppuvvvvvvvvvvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzBBBBBBBBBxvupppmiffdaa007(!,*++..",
7214"...  +P-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------P+  ...",
7215".....                                                                                                                                                                                                                       .....");
7216
7217
7218my ($scroll_w, $scroll_h);
7219
7220sub scale_buttons {
7221   my ( $width, $height, $corner, $delta ) =  @_ ; # ( $widht , $heigth );
7222
7223   my $dist_dig = 1; #distance between numbers digits
7224
7225   $buttons_h = int ( $height/2 - 4 ) - 1;                               # min:(15,15)   max:(22,22)
7226   $buttons_w = int ( ($width/2 - 4*$delta - $corner) * 2 / 9 - 1.2 );
7227
7228   if ( $buttons_w >= 22 ){ $buttons_w = 22; }  # Set the max w values.
7229   if ( $buttons_h >= 22 ){ $buttons_h = 22; }  # Set the max h values
7230
7231   $numbers_w = int( ( 2*$width/3 - 10*$dist_dig - $corner )/11 - 1);         # min:(9,12)   max:(13,18)
7232   $numbers_h = int ( $height/2 - $corner - 4 );
7233   if ( $numbers_w >= 13 ){ $numbers_w = 13; }  # Set the max w values.
7234   if ( $numbers_h >= 18 ){ $numbers_h = 18; }  # Set the max h values
7235
7236   ( $sound_size_w , $sound_size_h ) = ( $buttons_w - 5, $buttons_h - 3 );
7237   ( $loop_w , $loop_h ) = ( $buttons_w - 1, $buttons_h - 4 ); # min:(15,15) - (1,4) = (14,11)   max:(18,16)
7238   #print "loop_w = $loop_w ; loop_h = $loop_h\n";
7239   if ( $loop_w >= 18 ){ $loop_w = 18; }
7240   if ( $loop_h >= 16 ){ $loop_h = 16; }
7241
7242   ( $random_w , $random_h ) = ( $buttons_w - 3, $buttons_h - 3 );  # min:(12,12)   max:(17,17)
7243   if ( $random_w >= 17 ){ $random_w = 17; }
7244   if ( $random_h >= 17 ){ $random_h = 17; }
7245
7246   ( $posbar_w, $posbar_h ) = ( int( (2*$width/3) / 8 - 2 ) , int( $height/10 + 1 ) ); # min:(12,5)   max:(20,8)
7247   if ( $posbar_w >= 20 ){ $posbar_w = 20; }
7248   if ( $posbar_h >= 8 ){ $posbar_h = 8; }
7249   #print "buttons_h = $buttons_h ; buttons_w = $buttons_w ; numbers_w = $numbers_w ; numbers_h = $numbers_h\n";
7250   #print "posbar_w = $posbar_w ; posbar_h = $posbar_h\n";
7251
7252   $scroll_h = $buttons_h - 2;
7253   $scroll_h = 16 if $scroll_h >= 16;
7254
7255   if ( $buttons_w <= 5 or $buttons_h <= 3 ){ return; }
7256   scale_pixbuf();
7257}
7258
7259sub scale_pixbuf {
7260   $pixbuf_buttons  = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@buttons);
7261   $pixbuf_buttons  = $pixbuf_buttons->scale_simple ( 4*$buttons_w , 2*$buttons_h, 'bilinear');
7262   $pixbuf_buttons2 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@buttons2);
7263   $pixbuf_buttons2 = $pixbuf_buttons2->scale_simple ( 4*$buttons_w , 2*$buttons_h, 'bilinear');
7264   $pixbuf_buttons3 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@buttons3);
7265   $pixbuf_buttons3 = $pixbuf_buttons3->scale_simple ( 4*$buttons_w , 2*$buttons_h, 'bilinear');
7266
7267   $pixbuf_sound = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@sound);
7268   $pixbuf_sound = $pixbuf_sound->scale_simple ( $sound_size_w, $sound_size_h, 'bilinear');
7269
7270   $pixbuf_numbers  = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@numbers2);
7271   $pixbuf_numbers  = $pixbuf_numbers->scale_simple ( 14*$numbers_w , 1*$numbers_h, 'bilinear');
7272   $pixbuf_numbers2 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@numbers3);
7273   $pixbuf_numbers2 = $pixbuf_numbers2->scale_simple ( 14*$numbers_w , 1*$numbers_h, 'bilinear');
7274
7275   $pixbuf_posbar = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@posbar);
7276   $pixbuf_posbar = $pixbuf_posbar->scale_simple ( $posbar_w, $posbar_h, 'bilinear');
7277
7278   $pixbuf_loop  = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@loop);
7279   $pixbuf_loop  = $pixbuf_loop->scale_simple ( $loop_w, $loop_h, 'bilinear'); # hyper, bilinear
7280   $pixbuf_loop2 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@loop2);
7281   $pixbuf_loop2 = $pixbuf_loop2->scale_simple ( $loop_w, $loop_h, 'bilinear');
7282
7283   $pixbuf_random = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@random);
7284   $pixbuf_random = $pixbuf_random->scale_simple ( $random_w, $random_h, 'bilinear'); # hyper, bilinear
7285   $pixbuf_random2 = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@random2);
7286   $pixbuf_random2 = $pixbuf_random2->scale_simple ( $random_w, $random_h, 'bilinear');
7287}
7288
7289# see /usr/share/doc/perl-Gtk2-1.080/gtk-demo/drawingarea.pl and examples/scribble.pl
7290# GtkDrawingArea is a blank area where you can draw custom displays
7291# of various kinds.   See man Gtk2::Gdk::Drawable, Gtk2::Curve
7292
7293$da = Gtk2::DrawingArea->new;
7294$da->set_size_request (170, 40); # set a minimum size: (width,height) = (170, 40)
7295$hbox4->pack_start ($da, $false, $false, 0);
7296$da->signal_connect ( 'expose_event'         => \&expose_event_cdplayer);
7297$da->signal_connect ( 'configure_event'      => \&configure_event_cdplayer);
7298$da->signal_connect ( 'button_press_event'   => \&button_press_event_cdplayer);
7299$da->signal_connect ( 'button_release_event' => \&button_release_event_cdplayer);
7300$da->signal_connect ( 'motion_notify_event'  => \&motion_notify_event_cdplayer);
7301$da->set_events ([ @{ $da->get_events },
7302                      'exposure-mask',
7303                      'leave-notify-mask',
7304                      'button-press-mask',
7305		      'button-release-mask',
7306		      'pointer-motion-mask',
7307		      #'pointer-motion-hint-mask',
7308		      #'all-events-mask'
7309		 ]);
7310$da->set_sensitive($false);
7311$da->show;
7312
7313#  ---  Colors   ---  # See <man Gtk2::Gdk::Color>
7314
7315my $gc_bg; my $gc_blue; my $gc_green;
7316my $gc_skin; my $gc_skin2; my $color_skin2;
7317
7318# #E6EDBD = (230,237,189) : yellow   #FFFFFF=(255,255,255):white   #000000=(0,0,0):black
7319my $color_bg    = Gtk2::Gdk::Color->parse ($color_bg_cdplay);         # #E6EDBD , background color default
7320my $color_blue  = Gtk2::Gdk::Color->new (85*256, 90*256, 190*256);    #  RGB = 85 90 190 ; my color blue
7321my $color_green = Gtk2::Gdk::Color->new (167*256, 177*256, 190*256);  # '#A7B1BE' ; my green color
7322
7323my $delta    = 2;  # distance inter buttons
7324my $dista    = 7;  # distance (shift) from center
7325my $corner   = 4;
7326my $dist_dig = 1;  # distance between numbers digits
7327
7328# text scrolling position on the $da
7329my ($xdest, $ydest); # inicial position on the $drawing_area
7330
7331#numbers position on the $da
7332my $xdest_numbers; my $ydest_numbers;
7333
7334# progress bar of elapsed time; position on the $da
7335my $xdest_posbar; my $ydest_posbar; my $posbar_length;
7336
7337# loop button; position on the $da
7338my $xdest_loop; my $ydest_loop;
7339
7340# random button; position on the $da
7341my $xdest_random; my $ydest_random;
7342
7343# sound button; position on the $da
7344my $xdest_sound; my $ydest_sound;
7345
7346# buttons (Stop button); position on the $da
7347my $xdest_buttons; my $ydest_buttons;
7348
7349# Pixmap for cd player
7350my ($pixmap, $pixmap_mask) = (undef, undef);
7351
7352sub expose_event_cdplayer {
7353   my ($widget, $event ) = @_;    # $widget = $da = Gtk2::DrawingArea
7354                                  # $drawable = $widget->state;  Gtk2::Gdk::Drawable
7355
7356   $widget->window->draw_drawable ($widget->style->fg_gc($widget->state), # (Gtk2::Gdk::GC)
7357                                  $pixmap,
7358                                  # Only copy the area that was exposed.
7359                                  $event->area->x, $event->area->y,       # $xsrc,  $ysrc,
7360                                  $event->area->x, $event->area->y,       # $xdest, $ydest
7361                                  $event->area->width, $event->area->height);
7362   return $false;
7363}
7364
7365# Redraw the screen from the pixmap
7366sub configure_event_cdplayer {
7367   my ($widget, $event, @color) = @_;     # $widget = $da = Gtk2::DrawingArea
7368                                          # $drawable = $widget->state;  Gtk2::Gdk::Drawable
7369					  # color = Gtk2::Gdk::Color
7370
7371   my $height = $widget->allocation->height;  # height of $widget $da
7372   my $width  = $widget->allocation->width;
7373
7374   # background color definition
7375   $gc_bg = Gtk2::Gdk::GC->new ($widget->window); #  ($da->window) = Gtk2::Gdk::Drawable
7376   $color_bg_cdplay = $color[0] if defined($color[0]);
7377   $color_bg = Gtk2::Gdk::Color->parse ($color_bg_cdplay);
7378   $gc_bg->set_rgb_fg_color ($color_bg);
7379
7380   # background skin color definition
7381   $gc_skin = Gtk2::Gdk::GC->new ($widget->window); #  ($da->window) = Gtk2::Gdk::Drawable
7382   $gc_skin->set_rgb_fg_color ( Gtk2::Gdk::Color->parse ('#C8C8C8') );
7383
7384   # background display skin color definition
7385   $gc_skin2 = Gtk2::Gdk::GC->new ($widget->window); #  ($da->window) = Gtk2::Gdk::Drawable
7386   $color_skin2 = Gtk2::Gdk::Color->parse ('#DBE1C2');
7387   $gc_skin2->set_rgb_fg_color ($color_skin2);
7388
7389   # my blue color definition
7390   $gc_blue = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
7391   $gc_blue->set_rgb_fg_color ($color_blue);
7392
7393   # my green color definition ; limite lines
7394   $gc_green = Gtk2::Gdk::GC->new ($widget->window);
7395   $gc_green->set_rgb_fg_color ($color_green);
7396
7397   # button color (shadow) - Static
7398   $buttons[3] = "&	c $color[1]"  if defined($color[1]);
7399   $color_cdplayer_shadow = $color[1] if defined($color[1]);
7400
7401   # button color (arrows) - Static
7402   $buttons[2]  = ".	c $color[2]"  if defined($color[2]);
7403   $color_cdplayer_arrows = $color[2] if defined($color[2]);
7404
7405   # button color - random ? - when active
7406   $random[2] = ".	c $color[2]" if defined($color[2]);
7407   $random[3] = "+	c $color[1]" if defined($color[1]);
7408
7409   # button color - loop - when active
7410   $loop[2] = ".	c $color[1]" if defined($color[1]);
7411   $loop[3] = "+	c $color[2]" if defined($color[2]);
7412
7413   # button color (shadow) - Motion
7414   $buttons2[3] = "&	c $color[3]" if defined($color[3]);
7415   $color_cdplayer_shadow_motion = $color[3] if defined($color[3]);
7416
7417   # button color (arrows) - Motion
7418   $buttons2[2] = ".	c $color[4]" if defined($color[4]);
7419   $color_cdplayer_arrows_motion = $color[4] if defined($color[4]);
7420
7421   # button color (shadow) - Pressed
7422   $buttons3[3] = "&	c $color_cdplayer_shadow_motion";
7423
7424   # button color (digit)
7425   $numbers2[2] = ".	c $color[5]" if defined($color[5]);
7426   $color_cdplayer_digit = $color[5] if defined($color[5]);
7427
7428   # button color (digit_remaining)
7429   $numbers3[2] = ".	c $color[6]" if defined($color[6]);
7430   $color_cdplayer_digit_remaining = $color[6] if defined($color[6]);
7431
7432   $pixmap = undef if $pixmap; # get rid of the old one
7433   $pixmap_mask = undef if $pixmap_mask;
7434
7435   if ($show_cdplayer_skin and defined $window_cd_player){ # with skin_xTunes
7436      $widget->set_size_request (225, 90); # set a minimum size: (width,height) ; skin_xTunes
7437
7438      #(pixmap, mask) = Gtk2::Gdk::Pixmap->create_from_xpm ($drawable, $transparent_color, $filename)
7439      #($pixmap, $pixmap_mask) = Gtk2::Gdk::Pixmap->create_from_xpm ($widget->window, undef, '/tmp/skin_xTunes.xpm');
7440      ($pixmap, $pixmap_mask) = Gtk2::Gdk::Pixmap->create_from_xpm_d($widget->window, undef, @skin_xTunes);
7441
7442      #my $pixmap_widget = Gtk2::Image->new_from_pixmap ($gdkpixmap, $mask);
7443      $window_cd_player->shape_combine_mask( $pixmap_mask, 0, 0 ) if not $window_cd_player->get_decorated;
7444
7445      ($xdest, $ydest) = (74, 22);         # inicial position on the $drawing_area
7446      ($scroll_w, $scroll_h) = (138, 14);  # width and height of scrolling text
7447
7448      #numbers position on the $da
7449      $xdest_numbers = 16; $ydest_numbers = 22;
7450      $numbers_w = 9; $numbers_h = 13;
7451
7452      # progress bar of elapsed time; position on the $da
7453      $xdest_posbar = 10;
7454      $ydest_posbar = 55;
7455      $posbar_length = 204;
7456      $posbar_w = 18; $posbar_h = 6;
7457
7458      # buttons; position on the $da
7459      $xdest_buttons = 80;  # x_destination of Stop button on the $da
7460      $ydest_buttons = 63;
7461      ($buttons_w, $buttons_h) = (20,20);
7462
7463      # loop button; position on the $da
7464      $xdest_loop = 170;
7465      $ydest_loop = 36;
7466
7467      # random button; position on the $da
7468      $xdest_random = 152;
7469      $ydest_random = 36;
7470
7471      # sound button; position on the $da
7472      $xdest_sound = 186;
7473      $ydest_sound = 63;
7474      ($sound_size_w, $sound_size_h) = (18,20);
7475
7476      scale_pixbuf();
7477   }
7478   if (not defined $pixmap){ # without skin_xTunes
7479      $gc_skin  = $gc_bg;
7480      $gc_skin2 = $gc_bg;
7481      $color_skin2 = $color_bg;
7482
7483      $widget->set_size_request (170, 40);
7484      # pixmap = Gtk2::Gdk::Pixmap->new ($drawable, $width, $height, $depth) ; $depth (number of colors)
7485      $pixmap = Gtk2::Gdk::Pixmap->new ($widget->window, $width, $height, -1);
7486
7487      $pixmap->draw_rectangle ( $window->style->bg_gc($window->state), # fill with window bg color
7488                                $true, 0, 0, $width, $height);
7489
7490      my ($xc,$yc,$r,$n) = (6,6,6,10);  # ( x, y ) = ( xc, yc ) + r * ( cos(theta), sin(theta) )
7491
7492      my @coord_up_left    = coordinates_rounded_corner(         $xc,          $yc,$r,180,270,$n);
7493      my @coord_up_right   = coordinates_rounded_corner($width - $xc,          $yc,$r,270,360,$n);
7494      my @coord_down_left  = coordinates_rounded_corner($width - $xc,$height - $yc,$r,  0, 90,$n);
7495      my @coord_down_right = coordinates_rounded_corner(         $xc,$height - $yc,$r, 90,180,$n);
7496
7497      $pixmap->draw_polygon ($gc_bg, $true, # fill whith color $gc_bg
7498                                     @coord_up_left,   @coord_up_right,
7499				     @coord_down_left, @coord_down_right );
7500
7501      @coord_up_left    = coordinates_rounded_corner(         $xc - 0.1,          $yc - 0.1,$r,180,270,$n);
7502      @coord_up_right   = coordinates_rounded_corner($width - $xc - 0.1,          $yc - 0.1,$r,270,360,$n);
7503      @coord_down_left  = coordinates_rounded_corner($width - $xc - 0.1,$height - $yc - 0.1,$r,  0, 90,$n);
7504      @coord_down_right = coordinates_rounded_corner(         $xc - 0.1,$height - $yc - 0.1,$r, 90,180,$n);
7505
7506      # draw the limite lines
7507      # $drawable->draw_polygon ($gc, $filled, $x1, $y1, ...)
7508      $pixmap->draw_polygon ($gc_green, $false, # $widget->style->black_gc,
7509                                        @coord_up_left,   @coord_up_right,
7510				        @coord_down_left, @coord_down_right );
7511
7512      # refresh the sizes and scale
7513      scale_buttons($width, $height, $corner, $delta);
7514
7515      ($xdest, $ydest) = (int($corner + 5*($numbers_w + $dist_dig) + $numbers_w/2), int($corner/2) ); # inicial position on the $drawing_area
7516      ($scroll_w, $scroll_h) = (int($width - $xdest - $corner), $scroll_h);           # width and height of scrolling text
7517
7518      #numbers position on the $da
7519      $xdest_numbers = $corner; $ydest_numbers = $corner;
7520
7521      # progress bar of elapsed time; position on the $da
7522      $xdest_posbar  = int($corner + 5*($numbers_w + $dist_dig) + $numbers_w/2);
7523
7524      $ydest_posbar  = int($scroll_h + $corner);
7525      $posbar_length = int($width - $xdest_posbar - $corner);
7526
7527      # loop button; position on the $da
7528      $xdest_loop = $corner;
7529      $ydest_loop = $height - $corner - $loop_h;
7530
7531      # random button; position on the $da
7532      $xdest_random = $corner + $loop_w + 2;
7533      $ydest_random = $height - $corner - $random_h;
7534
7535      # sound button; position on the $da
7536      $xdest_sound = $width/2 - $sound_size_w - 5*$buttons_w/2 - 4*$delta + $dista;
7537      $ydest_sound = $height - $corner - $sound_size_h;
7538
7539      # buttons; position on the $da
7540      $xdest_buttons = $width/2 + $dista;  # Stop button x_destination drawing on the $da
7541      $ydest_buttons = $height - $buttons_h - 2;
7542   }
7543   draw_buttons( All => $true );
7544
7545   # show current time
7546   my ($hour,$min,$sec) = sec_to_time($count);
7547   draw_play_time($da, undef, ($min,$sec,$hour) );
7548
7549   # sound button
7550   draw_sound_button($da,undef);
7551
7552   # random button ?
7553   draw_random($da,undef);
7554
7555   # loop button
7556   draw_loop($da,undef);
7557
7558   # draw lapsed time, progress bar
7559   my $pct = 0;
7560   if ($total_time>0){$pct = number_value( sprintf("%.2f", $count/$total_time) );}
7561   draw_lapsed_time($da, undef, $pct);
7562
7563   # draw scrolling text
7564   show_scrolling_text();
7565
7566   # return TRUE because we've handled this event, so no further processing is required.
7567   return $true;
7568}
7569
7570sub coordinates_rounded_corner {    # arc of circle  - (10Jul2008)
7571   my ($xc,$yc,$r,$t1,$t2,$n) = @_; # ( x, y ) = ( xc, yc ) + r * ( cos(theta), sin(theta) ) :polar coordinate
7572   my @coordinates = ();
7573   my $pi = 3.141592654;
7574
7575   foreach my $i ( 0 .. $n ) {  # arc is divided by n equal segments
7576      my $theta_i = ( $t1 + ($i/$n)*($t2 - $t1) )*($pi/180);
7577      my $x_i = number_value( sprintf("%.4f", $xc + $r * cos($theta_i)) );  # sin(), cos(): see man perlfunc
7578      my $y_i = number_value( sprintf("%.4f", $yc + $r * sin($theta_i)) );
7579      push @coordinates, ( $x_i, $y_i );
7580   }
7581   #foreach my $i ( 0 .. 2*$n+1 ) { print "\$coordinates($i) = $coordinates[$i] \n"; }
7582   return @coordinates;
7583}
7584
7585my $button_press_stop    = $false; my $button_press_play  = $false; my $button_press_pause  = $false;
7586my $button_press_forward = $false; my $button_press_eject = $false; my $button_press_rewind = $false;
7587my $button_press_next    = $false; my $button_press_back  = $false;
7588
7589sub button_release_event_cdplayer {
7590   # Set the default color to all buttons that is released.
7591   if    ($button_press_stop   ){ draw_buttons(Draw_Stop => $true   ); $button_press_stop = $false;   }
7592   elsif ($button_press_play   ){ draw_buttons(Draw_Play => $true   ); $button_press_play = $false;   }
7593   elsif ($button_press_pause  ){ draw_buttons(Draw_Pause => $true  ); $button_press_pause = $false;  }
7594   elsif ($button_press_forward){ draw_buttons(Draw_Forward => $true); $button_press_forward = $false;}
7595   elsif ($button_press_eject  ){ draw_buttons(Draw_Eject => $true  ); $button_press_eject = $false;  }
7596   elsif ($button_press_rewind ){ draw_buttons(Draw_Rewind => $true ); $button_press_rewind = $false; }
7597   elsif ($button_press_next   ){ draw_buttons(Draw_Next => $true   ); $button_press_next = $false;   }
7598   elsif ($button_press_back   ){ draw_buttons(Draw_Back => $true   ); $button_press_back = $false;   }
7599   return $true;
7600}
7601
7602sub draw_buttons { # Get the 8 buttons from @buttons. Redraw only the button that is pressed.
7603   my %args = (
7604        Press_Stop => $false,   Draw_Stop => $false,    Shadow_Stop => $false, # If 'Press_Stop = $true' then change the button color
7605        Press_Play => $false,   Draw_Play => $false,    Shadow_Play => $false, # default value,
7606	Press_Pause => $false,  Draw_Pause => $false,   Shadow_Pause => $false,
7607	Press_Forward => $false,Draw_Forward => $false,	Shadow_Forward => $false,
7608	Press_Eject => $false,  Draw_Eject => $false,   Shadow_Eject => $false,
7609	Press_Rewind => $false, Draw_Rewind => $false,  Shadow_Rewind => $false,
7610	Press_Next => $false,   Draw_Next => $false,    Shadow_Next => $false,
7611	Press_Back => $false,	Draw_Back => $false,    Shadow_Back => $false,
7612	All => $false,	 # draw all buttons
7613        @_,              # argument pair list goes here
7614	      );
7615   my $widget = $da;
7616   my $height = $widget->allocation->height;  # height of $widget $da
7617   my $width  = $widget->allocation->width ;
7618   my $pixbuf_copy = $pixbuf_buttons;
7619   # $buttons_h is the height of buttons and $buttons_w is the width of buttons
7620
7621   my @update_rect;
7622   $update_rect[0] = $xdest_buttons;
7623   $update_rect[1] = $ydest_buttons;
7624   $update_rect[2] = $buttons_w;
7625   $update_rect[3] = $buttons_h;
7626   #$widget->queue_draw_area (@update_rect);
7627   return if not defined $pixmap;
7628
7629   # -------------------- 8 buttons ---------------------- #
7630   if ( $args{Draw_Stop} or $args{All} ){  # Draw independently the 8 buttons
7631
7632      $update_rect[0] = $xdest_buttons - 1*$buttons_w/2 + 0*$delta;
7633      $pixmap->draw_rectangle ($gc_skin, $true, @update_rect ); # erase first
7634      if    ($args{Shadow_Stop}){ $pixbuf_copy = $pixbuf_buttons2; } # Choose the color button
7635      elsif ($args{Press_Stop} ){ $pixbuf_copy = $pixbuf_buttons3; }
7636      # stop button , on the center of display
7637      # $drawable->draw_pixbuf ($gc, $pixbuf, $src_x, $src_y, $dest_x, $dest_y, $width, $height, $dither, $x_dither, $y_dither)
7638      $pixmap->draw_pixbuf ($gc_skin, $pixbuf_copy, 0*$buttons_w, 0*$buttons_h, @update_rect, 'normal', 0, 0);
7639      $widget->queue_draw_area (@update_rect);
7640   }
7641   if ( $args{Draw_Play} or $args{All} ){
7642
7643      $update_rect[0] = $xdest_buttons + 1*$buttons_w/2 + 1*$delta;
7644      $pixmap->draw_rectangle ($gc_skin, $true, @update_rect ); # erase first
7645      if    ($args{Shadow_Play}){ $pixbuf_copy = $pixbuf_buttons2; } # Choose the color button
7646      elsif ($args{Press_Play} ){ $pixbuf_copy = $pixbuf_buttons3; }
7647      # play or pause button
7648      my $button_y = 2*$buttons_w;
7649      unless ( $pause eq $true or $playing_music eq $false ){ $button_y = 1*$buttons_w; }
7650      $pixmap->draw_pixbuf ($gc_skin, $pixbuf_copy, $button_y, 0*$buttons_h, @update_rect, 'normal', 0, 0);
7651      $widget->queue_draw_area (@update_rect);
7652   }
7653   if ( $args{Draw_Forward}  or $args{All} ){
7654      $update_rect[0] = $xdest_buttons + 3*$buttons_w/2 + 2*$delta;
7655      $pixmap->draw_rectangle ($gc_skin, $true, @update_rect ); # erase first
7656      if    ($args{Shadow_Forward}){ $pixbuf_copy = $pixbuf_buttons2; }
7657      elsif ($args{Press_Forward} ){ $pixbuf_copy = $pixbuf_buttons3; }
7658      # fastforward button
7659      $pixmap->draw_pixbuf ($gc_skin, $pixbuf_copy, 0*$buttons_w, 1*$buttons_h, @update_rect, 'normal', 0, 0);
7660      $widget->queue_draw_area (@update_rect);
7661   }
7662   if ( $args{Draw_Next} or $args{All} ){
7663      $update_rect[0] = $xdest_buttons + 5*$buttons_w/2 + 3*$delta;
7664      $pixmap->draw_rectangle ($gc_skin, $true, @update_rect ); # erase first
7665      if    ($args{Shadow_Next}){ $pixbuf_copy = $pixbuf_buttons2; } # Choose the color button
7666      elsif ($args{Press_Next} ){ $pixbuf_copy = $pixbuf_buttons3; }
7667      # next button
7668      $pixmap->draw_pixbuf ($gc_skin, $pixbuf_copy, 3*$buttons_w, 0*$buttons_h, @update_rect, 'normal', 0, 0);
7669      $widget->queue_draw_area (@update_rect);
7670   }
7671   if ( $args{Draw_Eject} or $args{All} ){
7672      $update_rect[0] = $xdest_buttons + 7*$buttons_w/2 + 4*$delta;
7673      $pixmap->draw_rectangle ($gc_skin, $true, @update_rect ); # erase first
7674      if    ($args{Shadow_Eject}){ $pixbuf_copy = $pixbuf_buttons2; } # Choose the color button
7675      elsif ($args{Press_Eject} ){ $pixbuf_copy = $pixbuf_buttons3; }
7676      # eject button
7677      $pixmap->draw_pixbuf ($gc_skin, $pixbuf_copy, 2*$buttons_w, 1*$buttons_h, @update_rect, 'normal', 0, 0);
7678      $widget->queue_draw_area (@update_rect);
7679   }
7680   if ( $args{Draw_Rewind} or $args{All} ){
7681      $update_rect[0] = $xdest_buttons - 3*$buttons_w/2 - 1*$delta;
7682      $pixmap->draw_rectangle ($gc_skin, $true, @update_rect ); # erase first
7683      if    ($args{Shadow_Rewind}){ $pixbuf_copy = $pixbuf_buttons2; } # Choose the color button
7684      elsif ($args{Press_Rewind} ){ $pixbuf_copy = $pixbuf_buttons3; }
7685      # rewind button
7686      $pixmap->draw_pixbuf ($gc_skin, $pixbuf_copy, 1*$buttons_w, 1*$buttons_h, @update_rect, 'normal', 0, 0);
7687      $widget->queue_draw_area (@update_rect);
7688   }
7689   if ( $args{Draw_Back} or $args{All} ){
7690      $update_rect[0] = $xdest_buttons - 5*$buttons_w/2 - 2*$delta;
7691      $pixmap->draw_rectangle ($gc_skin, $true, @update_rect ); # erase first
7692      if    ($args{Shadow_Back}){ $pixbuf_copy = $pixbuf_buttons2; } # Choose the color button
7693      elsif ($args{Press_Back} ){ $pixbuf_copy = $pixbuf_buttons3; }
7694      # back button
7695      $pixmap->draw_pixbuf ($gc_skin, $pixbuf_copy, 3*$buttons_w, 1*$buttons_h, @update_rect, 'normal', 0, 0);
7696      $widget->queue_draw_area (@update_rect);
7697   }
7698   #while (Gtk2->events_pending()) {Gtk2->main_iteration()};  NEVER use this here
7699}
7700
7701sub draw_play_time {
7702   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
7703
7704   return if not defined $pixmap;
7705   #draw_play_time($da, undef, ($min,$sec,$hour) );
7706   my ($min, $sec, $hour) = @data; # the format "00:00" have 5 digits
7707
7708   my ($digit1,$digit2) = (13*$numbers_w,13*$numbers_w); #start value with background color
7709   my ($digit3,$digit4) = (13*$numbers_w,13*$numbers_w);
7710   my ($digit5,$digit6) = (13*$numbers_w,13*$numbers_w);
7711   my $two_points = 11*$numbers_w; # " : "
7712   my $division   = 10*$numbers_w; # " / "
7713
7714   if ( $min != -2 ){  #  not blink - n�o piscar
7715      $digit1 = ( int($min/10) )*$numbers_w;
7716      $digit2 = ( $min % 10    )*$numbers_w;
7717      $digit3 = ( int($sec/10) )*$numbers_w;  # 35 : int($sec/10) $sec%10
7718      $digit4 = ( $sec % 10    )*$numbers_w;
7719      $digit5 = ( int($hour/10) )*$numbers_w if defined($hour);
7720      $digit6 = ( $hour % 10    )*$numbers_w if defined($hour);
7721   }
7722   my $height = $widget->allocation->height;  # height of $widget $da
7723   my $width  = $widget->allocation->width ;
7724   my $pixbuf_copy = $show_time_remaining ? $pixbuf_numbers2 : $pixbuf_numbers; # to change the colors.
7725
7726   my @update_rect;
7727   $update_rect[0] = $xdest_numbers; #$corner
7728   $update_rect[1] = $ydest_numbers;
7729   $update_rect[2] = $numbers_w;
7730   $update_rect[3] = $numbers_h;
7731
7732   #print "\$update_rect[1] = $update_rect[1]\n";
7733   #$widget->queue_draw_area (@update_rect);
7734
7735   # "00:00"; there are 5 digits; Each button have (width,height) = ($numbers_w, $numbers_h)
7736
7737   # erase all ; rectangle : $drawable->draw_rectangle ($gc, $filled, $x, $y, $width, $height); See <man Gtk2::Gdk::Drawable>
7738   $pixmap->draw_rectangle ($gc_skin2, $true, $xdest_numbers, $ydest_numbers, 5*($numbers_w + $dist_dig), $numbers_h); # erase all
7739
7740      # -------------------- 5 digits ---------------------- #
7741      # 1o. digit : $drawable->draw_pixbuf ($gc, $pixbuf, $src_x, $src_y, $dest_x, $dest_y, $width, $height, $dither, $x_dither, $y_dither)
7742      $update_rect[0] = $xdest_numbers + 0*($numbers_w + $dist_dig);
7743      $pixmap->draw_pixbuf ($gc_skin2, $pixbuf_copy, $digit1, 0*$numbers_h, @update_rect, 'normal', 0, 0);
7744
7745      # 2o. digit
7746      $update_rect[0] = $xdest_numbers + 1*($numbers_w + $dist_dig);
7747      $pixmap->draw_pixbuf ($gc_skin2, $pixbuf_copy, $digit2, 0*$numbers_h, @update_rect, 'normal', 0, 0);
7748
7749      # : Two points
7750      $update_rect[0] = $xdest_numbers + 2*($numbers_w + $dist_dig);
7751      $pixmap->draw_pixbuf ($gc_skin2, $pixbuf_copy, $two_points, 0*$numbers_h, @update_rect, 'normal', 0, 0);
7752
7753      # 3o. digit
7754      $update_rect[0] = $xdest_numbers + 3*($numbers_w + $dist_dig);
7755      $pixmap->draw_pixbuf ($gc_skin2, $pixbuf_copy, $digit3, 0*$numbers_h, @update_rect, 'normal', 0, 0);
7756
7757      # 4o. digit
7758      $update_rect[0] = $xdest_numbers + 4*($numbers_w + $dist_dig);
7759      $pixmap->draw_pixbuf ($gc_skin2, $pixbuf_copy, $digit4, 0*$numbers_h, @update_rect, 'normal', 0, 0);
7760
7761   # See man Gtk2::Widget
7762   $widget->queue_draw_area ($xdest_numbers, $ydest_numbers, 5*($numbers_w + $dist_dig), $numbers_h);
7763}
7764
7765
7766sub draw_lapsed_time { # progress bar of elapsed time
7767   my ($widget, $event, $pct) = @_;   # $widget = $da = Gtk2::DrawingArea
7768
7769   return if not defined $pixmap;
7770   my $height = $widget->allocation->height;  # height of $widget $da
7771   my $width = $widget->allocation->width;
7772
7773   # pct vary from 0.000 to 1.000
7774   my $max_length  = $posbar_length - $posbar_w; #posbar_w is the button width
7775   my $lapsed_time = $pct * $max_length;
7776
7777   my $rail_h = 2;
7778   my $y = $ydest_posbar;
7779   my $y_rail = int ( ( 2*$y + $posbar_h - $rail_h )/2 );
7780   #print "y = $y ; y_rail = $y_rail, posbar_h = $posbar_h\n";
7781
7782   # rectangle  $drawable->draw_rectangle ($gc, $filled, $x, $y, $width, $height); See <man Gtk2::Gdk::Drawable>
7783   $pixmap->draw_rectangle ($gc_skin,  $true,  $xdest_posbar - 1, $y,       $posbar_length + 2, $posbar_h );  # erase all first
7784   $pixmap->draw_rectangle ($gc_green, $false, $xdest_posbar,     $y_rail , $posbar_length,     $rail_h   );  # path, rail
7785   # posbar button
7786   # $drawable->draw_pixbuf ($gc, $pixbuf, $src_x, $src_y, $dest_x, $dest_y, $width, $height, $dither, $x_dither, $y_dither)
7787   $pixmap->draw_pixbuf ($gc_bg, $pixbuf_posbar, 0, 0,
7788                                 $xdest_posbar + $lapsed_time, $y,
7789				 $posbar_w, $posbar_h, 'normal', 0, 0);
7790   my @update_rect;
7791   $update_rect[0] = $xdest_posbar - 1;
7792   $update_rect[1] = $y;
7793   $update_rect[2] = $posbar_length + 2;
7794   $update_rect[3] = $posbar_h;
7795   $widget->queue_draw_area (@update_rect);
7796}
7797
7798sub draw_sound_button {
7799   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
7800                                       # @data is an array
7801   return if not defined $pixmap;
7802   my $height = $widget->allocation->height;  # height of $widget $da
7803   my $width  = $widget->allocation->width;
7804
7805   my @update_rect;
7806   $update_rect[0] = $xdest_sound;
7807   $update_rect[1] = $ydest_sound;
7808   $update_rect[2] = $sound_size_w;
7809   $update_rect[3] = $sound_size_h;
7810
7811   $pixmap->draw_rectangle ($gc_skin, $true, @update_rect); # erase first
7812   $pixmap->draw_pixbuf ($gc_skin, $pixbuf_sound, 0, 0, @update_rect, 'normal', 0, 0);  # sound button
7813   $widget->queue_draw_area (@update_rect); #update the $da = Gtk2::DrawingArea
7814}
7815
7816sub draw_random {
7817   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
7818                                       # @data is an array
7819   return if not defined $pixmap;
7820   my $height = $widget->allocation->height;  # height of $widget $da
7821   my $width  = $widget->allocation->width;
7822
7823   my $pixbuf_copy = $pixbuf_random;
7824   $pixbuf_copy = $play_random ? $pixbuf_random : $pixbuf_random2;
7825
7826   my @update_rect;
7827   $update_rect[0] = $xdest_random;  $update_rect[1] = $ydest_random;
7828   $update_rect[2] = $random_w;      $update_rect[3] = $random_h;
7829
7830   $pixmap->draw_rectangle ($gc_skin2, $true, @update_rect); # erase first
7831   $pixmap->draw_pixbuf ($gc_skin2, $pixbuf_copy, 0, 0, @update_rect, 'normal', 0, 0);
7832   $widget->queue_draw_area (@update_rect); #update the $da = Gtk2::DrawingArea
7833}
7834
7835sub draw_loop {
7836   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
7837                                       # @data is an array
7838   return if not defined $pixmap;
7839   my $height = $widget->allocation->height;  # height of $widget $da
7840   my $width  = $widget->allocation->width;
7841
7842   my $pixbuf_copy = $pixbuf_loop;
7843   $pixbuf_copy = $loop_tracks ? $pixbuf_loop : $pixbuf_loop2;
7844
7845   my @update_rect;
7846   $update_rect[0] = $xdest_loop;  $update_rect[1] = $ydest_loop;
7847   $update_rect[2] = $loop_w;      $update_rect[3] = $loop_h;
7848
7849   $pixmap->draw_rectangle ($gc_skin2, $true, @update_rect); # erase first
7850   $pixmap->draw_pixbuf ($gc_skin2, $pixbuf_copy, 0, 0, @update_rect, 'normal', 0, 0);
7851   $widget->queue_draw_area (@update_rect); #update the $da = Gtk2::DrawingArea
7852
7853   #if ($loop_tracks){print "Loop ON\n";}
7854   #else{print "Loop OFF\n";}
7855}
7856
7857#------------------ Scrolling Text -------------------#
7858#--------------------- Start -------------------------#
7859
7860my $timer_scroll; my $timer_wait;
7861my $pixmap_layout = undef;
7862my ($width_layout, $height_layout);
7863my $xsrc = 0; my $ysrc = 0;
7864my $sinal = 1; # sign: +1 or -1
7865my @array_text_layout; my @array_width_layout; my @array_height_layout;
7866my $scroll_count = 0; my $scroll_count_h3 = 0; my $ind1 = 0; # matrix indice
7867my $scrolling_type_temp;
7868
7869sub show_scrolling_text { # choose the scrolling direction
7870   return unless defined $scrolling_type;
7871
7872   @array_text_layout = (); @array_width_layout = (); @array_height_layout = (); # clear
7873
7874   my @all_scrolling = ( 'horinzontal1', 'horinzontal2', 'horinzontal3', 'vertical' ); # all scrolling types
7875   my $rand = int( rand($#all_scrolling + 1) );  # pick one random element: 0  <=  rand() <  $#all_scrolling + 1
7876
7877   $scrolling_type_temp = $scrolling_type eq 'random' ? $all_scrolling[$rand] : $scrolling_type;
7878   #print "scrolling_type = $scrolling_type ;;  scrolling_type_temp = $scrolling_type_temp\n";
7879
7880   #print "sub show_scrolling_text --> \$scrolling_type = $scrolling_type ; \$rand = $rand ; \$scrolling_type_temp = $scrolling_type_temp\n";
7881
7882   if    ( $scrolling_type_temp eq 'horinzontal1' or $scrolling_type_temp eq 'vertical'     ){ show_scrolling_text_special(); }
7883   elsif ( $scrolling_type_temp eq 'horinzontal2' or $scrolling_type_temp eq 'horinzontal3' ){ show_scrolling_text_normal();  }
7884   return $false;  # to stop the loop of $timer_wait for "loop on", "random on", ...
7885}
7886
7887sub show_scrolling_text_normal { # horizontal2 and horizontal3
7888   my ($pixmap_layout, $width_layout, $height_layout);
7889   my $row = $selected_row;
7890   my $scrol_text;
7891
7892   if ( defined $files_info[$row]{artist} ){
7893      $scrol_text = " *** $files_info[$row]{file}. ";
7894      $scrol_text .= "$files_info[$row]{artist} - "  if $files_info[$row]{artist};
7895      $scrol_text .= "$files_info[$row]{title} - "   if $files_info[$row]{title};
7896      $scrol_text .= "$files_info[$row]{album} - "   if $files_info[$row]{album};
7897
7898      if ($files_info[$row]{artist} eq "" and $files_info[$row]{title} eq "" and $files_info[$row]{album} eq ""){
7899	 (my $file = $files_info[$row]{filename}) =~ s/\.(...|flac)$//i; # remove extension_input
7900         $scrol_text .= "$file - "
7901      }
7902      $scrol_text .= "track $files_info[$row]{track} " if $files_info[$row]{track};
7903      $scrol_text .= "($files_info[$row]{length}) - $files_info[$row]{bitrate} kb/s ";
7904   }
7905
7906   ($pixmap_layout, $width_layout, $height_layout) = defined $scrol_text ? draw_layout( text => "$scrol_text" ) : draw_layout( );
7907   push @array_text_layout,  $pixmap_layout;
7908   push @array_width_layout, $width_layout;
7909   push @array_height_layout, $height_layout;
7910
7911   my $ind = ($ind1)%($#array_text_layout+1); # cyclic indices ;; always $ind = 0 ;; $ind is local
7912   draw_text_scrolling(layout => $array_text_layout[$ind], center => int(($scroll_w - $array_width_layout[$ind])/2) );
7913}
7914
7915sub show_scrolling_text_special { # horizontal1 or vertical
7916   my ($pixmap_layout, $width_layout, $height_layout);
7917   my $row = $selected_row;
7918
7919   if ( defined $files_info[$row]{artist} ){
7920
7921      my @all_artist = return_artist_name($files_info[$row]); # 'art1 & art2' return (art1,art2)
7922
7923      foreach my $artist ( @all_artist ) {
7924         next unless $artist;
7925	 ($pixmap_layout, $width_layout, $height_layout) = draw_layout( text => "$artist" );
7926         push @array_text_layout,  $pixmap_layout;
7927         push @array_width_layout, $width_layout;
7928	 push @array_height_layout, $height_layout;
7929      }
7930      foreach my $item ( 'title', 'album' ) {
7931         next unless $files_info[$row]{$item};
7932	 ($pixmap_layout, $width_layout, $height_layout) = draw_layout( text => "$files_info[$row]{$item}" );
7933         push @array_text_layout,  $pixmap_layout;
7934         push @array_width_layout, $width_layout;
7935	 push @array_height_layout, $height_layout;
7936      }
7937      if (@array_text_layout <= 0){
7938	 (my $file = $files_info[$row]{filename}) =~ s/\.(...|flac)$//i; # remove extension_input
7939	 ($pixmap_layout, $width_layout, $height_layout) = draw_layout( text => "$file" );
7940	 push @array_text_layout,  $pixmap_layout;
7941         push @array_width_layout, $width_layout;
7942	 push @array_height_layout, $height_layout;
7943      }
7944      if ($files_info[$row]{track}){
7945         ($pixmap_layout, $width_layout, $height_layout) = draw_layout( text => "track $files_info[$row]{track} ($files_info[$row]{length})" );
7946         push @array_text_layout,  $pixmap_layout;
7947         push @array_width_layout, $width_layout;
7948	 push @array_height_layout, $height_layout;
7949      }
7950      if ($files_info[$row]{frequency}){
7951         ($pixmap_layout, $width_layout, $height_layout) = draw_layout( text => "$files_info[$row]{frequency} Hz ($files_info[$row]{bitrate} kb/s)" );
7952         push @array_text_layout,  $pixmap_layout;
7953         push @array_width_layout, $width_layout;
7954	 push @array_height_layout, $height_layout;
7955      }
7956   }
7957   else{
7958      ($pixmap_layout, $width_layout, $height_layout) = draw_layout();
7959      push @array_text_layout,  $pixmap_layout;
7960      push @array_width_layout, $width_layout;
7961      push @array_height_layout, $height_layout;
7962   }
7963
7964   $ind1 = ($ind1)%($#array_text_layout+1); # cyclic indices ;; $ind1 is global
7965   draw_text_scrolling(layout => $array_text_layout[$ind1], center => int(($scroll_w - $array_width_layout[$ind1])/2) );
7966}
7967
7968sub draw_text_scrolling {
7969   my %args = (
7970	        widget     => $da,    # $widget = $da = Gtk2::DrawingArea
7971		event      => undef,
7972		scrolling  => $true,
7973		center     => int(($scroll_w - $width_layout)/2), # put the text on the center
7974                @_,                   # argument pair list goes here
7975	      );
7976   my $widget = $args{widget};
7977   my $text   = $args{text};
7978   $pixmap_layout = $args{layout};
7979   return if not defined $pixmap_layout;
7980
7981   # init values
7982   if ($scrolling_type_temp eq 'horinzontal2' or $scrolling_type_temp eq 'horinzontal3'){ $xsrc = 0; $scroll_count_h3 = 170; }
7983   $scroll_count = 0 if ( $scroll_count >= 3 ); # for 'horinzontal1' and 'vertical'
7984   $sinal = +1;
7985
7986   my @update_rect;
7987   $update_rect[0] = $xdest;
7988   $update_rect[1] = $ydest;
7989   $update_rect[2] = $scroll_w;
7990   $update_rect[3] = $scroll_h;
7991
7992   $pixmap->draw_rectangle ($gc_skin2, $true, @update_rect); # erase all first
7993   #$drawable->draw_drawable ($gc, $src, $xsrc, $ysrc, $xdest, $ydest, $width, $height)
7994   $pixmap->draw_drawable ($gc_skin2, $pixmap_layout, -$args{center}, 0, $xdest, $ydest, $scroll_w, $scroll_h); # draw the layout
7995   $widget->queue_draw_area (@update_rect);  # refresh
7996
7997   begin_scroll_loop(scrolling => $args{scrolling});
7998}
7999
8000sub begin_scroll_loop {
8001   my %args = (
8002		scrolling  => $true,
8003                @_,        # argument pair list goes here
8004	      );
8005
8006   if ( defined($timer_scroll) ){ Glib::Source->remove($timer_scroll);}  # to not duplicate the MainLoop
8007   if ( defined($timer_wait)   ){ Glib::Source->remove($timer_scroll);}  # to not duplicate "loop on", "random on", ...
8008   if ($args{scrolling}){   # 250 milliseconds = 0.250 second
8009      # integer = Glib::Timeout->add ($interval, $callback, $data=undef, $priority=G_PRIORITY_DEFAULT); see <man Glib::MainLoop>
8010      $timer_scroll = Glib::Timeout->add ($scrolling_text_speed, \&scroll_text_v, undef, 0)  if ($scrolling_type_temp eq 'vertical');
8011      $timer_scroll = Glib::Timeout->add ($scrolling_text_speed, \&scroll_text_h1, undef, 0) if ($scrolling_type_temp eq 'horinzontal1');
8012      $timer_scroll = Glib::Timeout->add ($scrolling_text_speed, \&scroll_text_h2, undef, 0) if ($scrolling_type_temp eq 'horinzontal2');
8013      $timer_scroll = Glib::Timeout->add ($scrolling_text_speed, \&scroll_text_h3, undef, 0) if ($scrolling_type_temp eq 'horinzontal3');
8014   }
8015}
8016
8017sub draw_layout { # return the $pixmap_layout->draw_layout_with_colors with text
8018   my %args = (
8019	        widget     => $da,    # $widget = $da = Gtk2::DrawingArea
8020		text       => " ***   artist - title - album - track (length) ",
8021                @_,                   # argument pair list goes here
8022	      );
8023   my $widget = $args{widget};
8024   my $text   = $args{text};
8025   return if not defined $pixmap;
8026
8027   my $height = $widget->allocation->height;  # height of $widget $da
8028   my $width = $widget->allocation->width;
8029
8030   #my $purple = Gtk2::Gdk::Color->parse ('purple');
8031   my $purple = Gtk2::Gdk::Color->parse ('#6419b8');
8032
8033   #my $desc = Gtk2::Pango::FontDescription->new();
8034   #my $context = Gtk2::Pango::Context->new();
8035   #my $layout = Gtk2::Pango::Layout->new ($context);
8036   my $layout = $widget->create_pango_layout ("");
8037   my $context = $layout->get_context;
8038   my $fontdesc = $context->get_font_description;
8039   # Choose the Fonts: "Sans", "Fixed", "Utopia", "Courier", "LucidaTypewriter", "Luxi Mono" etc
8040   $fontdesc->set_family ("Sans");
8041   $fontdesc->set_style ("normal");
8042
8043   # find the nice height size and change the font
8044   my $max_height = $scroll_h;
8045   $height_layout = $max_height - 1;  # start value
8046   my $max_font_size = 2;             # start height size
8047   while ( $height_layout <= $max_height ){  # to determine the maximum height size
8048       $fontdesc->set_size ( $max_font_size * PANGO_SCALE );
8049       $layout->set_text ("0�g�X_"); # only to get its width and height
8050       ($width_layout, $height_layout) = $layout->get_pixel_size;
8051       #print "height_layout = $height_layout ; max_height = $max_height ; max_font_size = $max_font_size\n";
8052       $max_font_size += 1 if ($height_layout <= $max_height) ;
8053   }
8054   $max_font_size -= 1 if ($height_layout > $max_height);
8055
8056   $fontdesc->set_size ( $max_font_size * PANGO_SCALE );
8057   $layout->set_text ($text);
8058   ($width_layout, $height_layout) = $layout->get_pixel_size;    # get its width and height ; global variable
8059   #print "-> height_layout = $height_layout <= max_height = $scroll_h ; max_font_size = $max_font_size\n";
8060
8061   $layout->set_text ($text." ");  # Put adicional pixels to fix border problem.
8062
8063   #pixmap = Gtk2::Gdk::Pixmap->new ($drawable, $width, $height, $depth)
8064   $pixmap_layout = Gtk2::Gdk::Pixmap->new ($widget->window, $width_layout, $height_layout, -1);
8065   #$drawable->draw_layout_with_colors ($gc, $x, $y, $layout, $foreground, $background); see <man Gtk2::Gdk::Drawable>
8066   $pixmap_layout->draw_layout_with_colors ($gc_bg, 0, 0, $layout, $purple, $color_skin2);
8067
8068   return ($pixmap_layout, $width_layout, $height_layout);
8069}
8070
8071sub scroll_text_h1 {
8072     my $incremental = $scrolling_text_incremental || 1; #  1 is default (1 pixel).
8073     my $sep = 16; # separation
8074
8075     $ind1 = ($ind1)%($#array_text_layout+1); # cyclic indices
8076     my $ind2 = $scrolling_text_orientation > 0 ? ($ind1 + 1)%($#array_text_layout+1) :                  # next crescent   : 0 1 2 3 0 1 2
8077                                                  ($ind1 + $#array_text_layout)%($#array_text_layout+1); # next decrescent : 0 3 2 1 0 3 2
8078
8079     $xsrc = $xsrc + $scrolling_text_orientation * abs($incremental); # $scrolling_text_orientation : + (up) ; - (down)
8080     $ysrc = 0;
8081
8082     if ( abs($xsrc) > int(($array_width_layout[$ind1] + $array_width_layout[$ind2])/2) + $sep ){
8083        $xsrc = 0;
8084     	$scroll_count += 1;
8085     }
8086     if ( $scroll_count < 4 ){ return $true; }
8087     if ( $scroll_count > 4 ){ $scroll_count = 0; $ind1 = $ind2; return $true; }
8088
8089     my @update_rect;
8090     $update_rect[0] = $xdest;     $update_rect[1] = $ydest;
8091     $update_rect[2] = $scroll_w;  $update_rect[3] = $scroll_h;
8092     $pixmap->draw_rectangle($gc_skin2, $true, @update_rect); # erase all first
8093
8094     my $center1h = int(($scroll_w - $array_width_layout[$ind1])/2);
8095
8096     my $i = $ind1;                             # the center indice
8097     my $ini = $center1h - $xsrc;
8098     my $remain = $scroll_w - $ini;
8099     while ( $remain > 0 ){                     # the right side from the center layout
8100        my $ind = ($i)%($#array_text_layout+1); # cyclic indices
8101	my $wid = $array_width_layout[$ind] + $sep;
8102	my $x0 = $ini > 0 ? 0 : abs($ini);
8103        $pixmap->draw_drawable ($gc_bg, $array_text_layout[$ind],                # $src (Gtk2::Gdk::Drawable)
8104                                        $x0, $ysrc,                              # $xsrc,  $ysrc,
8105				        $xdest + maximum($ini,0), $ydest,        # $xdest, $ydest
8106				        minimum($remain,$scroll_w), $scroll_h);  # $width, $height
8107	$remain -= $wid;
8108	$ini += $wid;
8109	$i += 1;
8110     }
8111
8112     $i = $ind1 + $#array_text_layout;          # the next decrescent array element : 0 3 2 1 0 3 2 1
8113     $remain = $center1h - $xsrc;
8114     while ( $remain > 0 ){                     # the left side from the center layout
8115        my $ind = ($i)%($#array_text_layout+1); # cyclic indices
8116	my $wid = $array_width_layout[$ind] + $sep;
8117        $remain -= $wid;
8118	my $x0 = $remain >= 0 ? 0 : abs($remain);
8119        $pixmap->draw_drawable ($gc_bg, $array_text_layout[$ind],                  # $src (Gtk2::Gdk::Drawable)
8120                                        $x0, $ysrc,                                # $xsrc,  $ysrc,
8121				        $xdest + maximum($remain,0), $ydest,       # $xdest, $ydest
8122				        minimum($wid - $x0,$scroll_w), $scroll_h); # $width, $height
8123	$i -= 1;
8124     }
8125
8126     $da->queue_draw_area (@update_rect); # refresh the area
8127     return $true;
8128}
8129
8130sub scroll_text_h2 {
8131     my $incremental = $scrolling_text_incremental || 1; #  1 is default (1 pixel).
8132     my $ind = ($ind1)%($#array_text_layout+1);          # $ind = 0, because the @array_width_layout has only one element
8133     my $center1h = int(($scroll_w - $array_width_layout[$ind])/2);
8134     my $wid = $array_width_layout[$ind];
8135
8136     $xsrc = $xsrc + $sinal * $scrolling_text_orientation * abs($incremental);
8137     $ysrc = 0;
8138
8139     my @update_rect;
8140     $update_rect[0] = $xdest;     $update_rect[1] = $ydest;
8141     $update_rect[2] = $scroll_w;  $update_rect[3] = $scroll_h;
8142     $pixmap->draw_rectangle($gc_skin2, $true, @update_rect);  # erase all first
8143
8144     if ( $center1h <= 0 ) {   # closed loop
8145        my $ini = $center1h - $xsrc;
8146        my $remain = $scroll_w - $ini;
8147        while ( $remain > 0 ){             # the right side from the center layout
8148	   my $x0 = $ini > 0 ? 0 : abs($ini);
8149           $pixmap->draw_drawable ($gc_bg, $array_text_layout[$ind],                # $src (Gtk2::Gdk::Drawable)
8150                                           $x0, $ysrc,                              # $xsrc,  $ysrc,
8151				           $xdest + maximum($ini,0), $ydest,        # $xdest, $ydest
8152				           minimum($remain,$scroll_w), $scroll_h);  # $width, $height
8153	   $remain -= $wid;
8154	   $ini += $wid;
8155        }
8156
8157        $remain = $center1h - $xsrc;
8158        while ( $remain > 0 ){             # the left side from the center layout
8159           $remain -= $wid;
8160	   my $x0 = $remain >= 0 ? 0 : abs($remain);
8161           $pixmap->draw_drawable ($gc_bg, $array_text_layout[$ind],                  # $src (Gtk2::Gdk::Drawable)
8162                                           $x0, $ysrc,                                # $xsrc,  $ysrc,
8163				           $xdest + maximum($remain,0), $ydest,       # $xdest, $ydest
8164				           minimum($wid - $x0,$scroll_w), $scroll_h); # $width, $height
8165        }
8166     }
8167     elsif ( $center1h > 0 ) {   # zig-zag, movimento de vai e vem.
8168        if ( abs($xsrc) > abs($center1h) ) { # when the layout touches one extreme, change the $sinal.
8169           $scroll_count_h3 = 0;
8170	   $sinal = -$sinal;
8171        }
8172
8173        $pixmap->draw_drawable ($gc_bg, $pixmap_layout,           # $src (Gtk2::Gdk::Drawable)
8174                                        $xsrc - $center1h, $ysrc, # $xsrc, $ysrc,
8175				        $xdest, $ydest,           # $xdest, $ydest
8176				        $scroll_w, $scroll_h);    # $width, $height
8177     }
8178
8179     $da->queue_draw_area (@update_rect); # refresh the area
8180     return $true;
8181}
8182
8183sub scroll_text_h3 {
8184     my $incremental = $scrolling_text_incremental || 1; #  1 is default (1 pixel).
8185     my $center1h = int( ($scroll_w - $width_layout)/2 );
8186     $ysrc = 0;
8187
8188     $scroll_count_h3 += 1;
8189     if ( $scroll_count_h3 < 200 or $center1h >= 0 ) { return $true; }
8190
8191     $xsrc = $xsrc + $sinal * $scrolling_text_orientation * abs($incremental);
8192     $xsrc = 0 if ( abs($xsrc) > abs($center1h) + abs($incremental) );
8193
8194     if ( abs($xsrc) > abs($center1h) ) { # when the layout touches one extreme, the $sinal is changed.
8195        $scroll_count_h3 = 0;
8196	$sinal = -$sinal;
8197     }
8198
8199     my @update_rect;
8200     $update_rect[0] = $xdest;     $update_rect[1] = $ydest;
8201     $update_rect[2] = $scroll_w;  $update_rect[3] = $scroll_h;
8202     $pixmap->draw_rectangle($gc_skin2, $true, @update_rect);  # erase all first
8203
8204     $pixmap->draw_drawable ($gc_bg, $pixmap_layout,           # $src (Gtk2::Gdk::Drawable)
8205                                     $xsrc - $center1h, $ysrc, # $xsrc, $ysrc,
8206				     $xdest, $ydest,           # $xdest, $ydest
8207				     $scroll_w, $scroll_h);    # $width, $height
8208
8209     $da->queue_draw_area (@update_rect); # refresh the area
8210     return $true;
8211}
8212
8213sub scroll_text_v {
8214     my $incremental = $scrolling_text_incremental || 1; #  1 is default (1 pixel).
8215     my $sep = 2; # separation
8216
8217     $ind1 = ($ind1)%($#array_text_layout+1); # cyclic indices
8218     my $ind2 = $scrolling_text_orientation > 0 ? ($ind1 + 1)%($#array_text_layout+1) :                  # next crescent   : 0 1 2 3 0 1 2
8219                                                  ($ind1 + $#array_text_layout)%($#array_text_layout+1); # next decrescent : 0 3 2 1 0 3 2
8220
8221     $xsrc = 0;
8222     $ysrc = $ysrc - $scrolling_text_orientation * abs($incremental); # $scrolling_text_orientation : + (down) ; - (up)
8223
8224     if ( abs($ysrc) > $array_height_layout[$ind1] + $sep ){
8225        $ysrc = 0;
8226     	$scroll_count += 1;
8227     }
8228     if ( $scroll_count < 14 ){ return $true; }
8229     if ( $scroll_count > 14 ){ $scroll_count = 0; $ind1 = $ind2; return $true;}
8230
8231     my $center1 = int(($scroll_w - $array_width_layout[$ind1])/2); $center1 = 0 if $center1 < 0;
8232     my $center2 = int(($scroll_w - $array_width_layout[$ind2])/2); $center2 = 0 if $center2 < 0;
8233
8234     my @update_rect;
8235     $update_rect[0] = $xdest;     $update_rect[1] = $ydest;
8236     $update_rect[2] = $scroll_w;  $update_rect[3] = $scroll_h;
8237     $pixmap->draw_rectangle($gc_skin2, $true, @update_rect);      # erase all first
8238
8239     $pixmap->draw_drawable ($gc_bg, $array_text_layout[$ind1],    # $src (Gtk2::Gdk::Drawable)
8240                                     $xsrc, $ysrc,                 # $xsrc,  $ysrc,
8241				     $xdest + $center1, $ydest,    # $xdest, $ydest
8242				     $scroll_w, $scroll_h);        # $width, $height
8243     if ( $ysrc > $sep ) {      # up
8244        $pixmap->draw_drawable ($gc_bg, $array_text_layout[$ind2], # $src (Gtk2::Gdk::Drawable)
8245                                        $xsrc, 0,                  # $xsrc, $ysrc,
8246				        $xdest + $center2, $ydest + $scroll_h - ( $ysrc - $sep ),   # $xdest, $ydest
8247				        $scroll_w, $ysrc - $sep);  # $width, $height
8248     }
8249     if ( $ysrc + $sep < 0 ) {  # down
8250        $pixmap->draw_drawable ($gc_bg, $array_text_layout[$ind2],        # $src (Gtk2::Gdk::Drawable)
8251                                        $xsrc, $array_height_layout[$ind2] + $ysrc + $sep,  # $xsrc, $ysrc,
8252				        $xdest + $center2, $ydest,        # $xdest, $ydest
8253				        $scroll_w, abs($ysrc + $sep));    # $width, $height
8254     }
8255     $da->queue_draw_area (@update_rect); # refresh the area
8256     return $true;
8257}
8258
8259#--------------------- Final -------------------------#
8260#------------------ Scrolling Text -------------------#
8261
8262#----------- CD player Color Selection ---------------#
8263#--------------------- Start -------------------------#
8264
8265# see /usr/share/doc/perl-Gtk2-1.054/gtk-demo/colorsel.pl
8266
8267my $window_cdplayer_color; my $notebook_cdplayer; my $button_cdplayer_color;
8268my $darea1; my $darea2; my $darea3; my $darea4;
8269my $darea5; my $darea6; my $darea7;
8270
8271sub make_window_cdplayer_color {
8272
8273   $window_cdplayer_color = Gtk2::Window->new('toplevel'); # 'toplevel' or 'popup'
8274   $window_cdplayer_color->set_title ("gnormalize player");
8275   $window_cdplayer_color->set_border_width (4);
8276   $window_cdplayer_color->signal_connect (destroy => \&Quit_window_cdplayer_color);
8277   $window_cdplayer_color->set_position ('none'); # none, center-always, center-on-parent , mouse
8278   $window_cdplayer_color->set( 'resizable' => $true, 'allow-shrink' => $false, 'allow-grow' => $true);
8279   $window_cdplayer_color->realize;
8280
8281   my $vbox_cdplayer_color = Gtk2::VBox->new ($false, 8);
8282   $vbox_cdplayer_color->set_border_width (2);
8283   $window_cdplayer_color->add ($vbox_cdplayer_color);
8284
8285   my $frame_cdplayer_color = Gtk2::Frame->new;
8286   $frame_cdplayer_color->set_shadow_type ('in');
8287
8288   my $frame_cdplayer_players = Gtk2::Frame->new;
8289   $frame_cdplayer_players->set_shadow_type ('in');
8290
8291   my $frame_cdplayer_columns = Gtk2::Frame->new;
8292   $frame_cdplayer_columns->set_shadow_type ('in');
8293
8294   my $frame_cdplayer_skins = Gtk2::Frame->new;
8295   $frame_cdplayer_skins->set_shadow_type ('in');
8296
8297   #----Notebook----#
8298   $notebook_cdplayer = Gtk2::Notebook->new;
8299   $notebook_cdplayer->set( 'homogeneous' => $true, 'scrollable' => $true );
8300   $notebook_cdplayer->append_page( $frame_cdplayer_color,   $langs{all_tab11} );   # Colors
8301   $notebook_cdplayer->append_page( $frame_cdplayer_players, $langs{all_tab12} );   # Players
8302   $notebook_cdplayer->append_page( $frame_cdplayer_columns, $langs{all_tab13} );   # Columns
8303   $notebook_cdplayer->append_page( $frame_cdplayer_skins,   $langs{all_tab14} );   # Skins
8304   $vbox_cdplayer_color->pack_start( $notebook_cdplayer, $true, $true, 0 );
8305
8306   #$notebook_cd_player_num = $notebook->get_current_page;
8307   #$notebook->set_current_page ($notebook_cd_player_num); # see this command on the final
8308
8309   #$table = new Gtk::Table( $num_rows, $num_columns, $homogeneous );
8310   my $table_cdplayer_color = new Gtk2::Table (6, 3, 0);
8311   $table_cdplayer_color->set_border_width (4);
8312   $table_cdplayer_color->set_row_spacings(4);
8313   $table_cdplayer_color->set_col_spacings(2);
8314   $frame_cdplayer_color->add( $table_cdplayer_color );
8315
8316   my $label_cdplayer_color1 = Gtk2::Label->new("Background color: ");
8317   $label_cdplayer_color1->set_alignment( 1.0, 0.5 );
8318   $table_cdplayer_color->attach ($label_cdplayer_color1, 0, 1, 0, 1, 'fill', 'fill', 0, 0);
8319
8320   my @def_size = (90,24); # color button size
8321
8322   $darea1 = Gtk2::DrawingArea->new;
8323   #my $color_bg    = Gtk2::Gdk::Color->parse ($color_bg_cdplay);
8324   $darea1->modify_bg ('normal', $color_bg ); # set the color
8325
8326   #my $button_cdplayer_color = Gtk2::Button->new;
8327   $button_cdplayer_color = Gtk2::Button->new;
8328   $button_cdplayer_color->add ($darea1);  # ->set_image ($darea1);
8329   $button_cdplayer_color->set_size_request (@def_size); # set a minimum size
8330   $button_cdplayer_color->signal_connect (clicked => \&change_color_cdplayer_bg, "display" );
8331   $table_cdplayer_color->attach ($button_cdplayer_color, 1, 2, 0, 1, 'shrink', 'fill', 0, 0);
8332
8333   #-------- Static --------#
8334
8335   my $hsep = Gtk2::HSeparator->new;
8336   $table_cdplayer_color->attach ($hsep, 0, 2, 1, 2, ['fill','expand'], 'fill', 0, 0);
8337   my $label_stat = Gtk2::Label->new("Static Appearance");
8338   $table_cdplayer_color->attach ($label_stat, 0, 2, 2, 3, 'fill', 'fill', 0, 0);
8339
8340   my $label_cdplayer_color2 = Gtk2::Label->new("Buttons (shadow): ");
8341   $label_cdplayer_color2->set_alignment( 1.0, 0.5 );
8342   $table_cdplayer_color->attach ($label_cdplayer_color2, 0, 1, 3, 4, 'fill', 'fill', 0, 0);
8343
8344   $darea2 = Gtk2::DrawingArea->new;
8345   $darea2->modify_bg ('normal', Gtk2::Gdk::Color->parse ($color_cdplayer_shadow) ); # set the color
8346
8347   my $button_cdplayer_color2 = Gtk2::Button->new;
8348   $button_cdplayer_color2->add ($darea2);
8349   $button_cdplayer_color2->set_size_request (@def_size); # set a minimum size
8350   $button_cdplayer_color2->signal_connect (clicked => \&change_color_cdplayer_bg, "shadow" );
8351   $table_cdplayer_color->attach ($button_cdplayer_color2, 1, 2, 3, 4, 'shrink', 'fill', 0, 0);
8352
8353   my $label_cdplayer_color3 = Gtk2::Label->new("Buttons (arrows): ");
8354   $label_cdplayer_color3->set_alignment( 1.0, 0.5 );
8355   $table_cdplayer_color->attach ($label_cdplayer_color3, 0, 1, 4, 5, 'fill', 'fill', 0, 0);
8356
8357   $darea3 = Gtk2::DrawingArea->new;
8358   $darea3->modify_bg ('normal', Gtk2::Gdk::Color->parse ($color_cdplayer_arrows) ); # set the color
8359
8360   my $button_cdplayer_color3 = Gtk2::Button->new;
8361   $button_cdplayer_color3->add ($darea3);
8362   $button_cdplayer_color3->set_size_request (@def_size); # set a minimum size
8363   $button_cdplayer_color3->signal_connect (clicked => \&change_color_cdplayer_bg, "arrows" );
8364   $table_cdplayer_color->attach ($button_cdplayer_color3, 1, 2, 4, 5, 'shrink', 'fill', 0, 0);
8365
8366   #-------- Motion --------#
8367
8368   my $hsep2 = Gtk2::HSeparator->new;
8369   $table_cdplayer_color->attach ($hsep2, 0, 2, 5, 6, ['fill','expand'], 'fill', 0, 0);
8370   my $label_mov = Gtk2::Label->new("Motion Appearance");
8371   $table_cdplayer_color->attach ($label_mov, 0, 2, 6, 7, 'fill', 'fill', 0, 0);
8372
8373   my $label_cdplayer_color4 = Gtk2::Label->new("Buttons (shadow): ");
8374   $label_cdplayer_color4->set_alignment( 1.0, 0.5 );
8375   $table_cdplayer_color->attach ($label_cdplayer_color4, 0, 1, 7, 8, 'fill', 'fill', 0, 0);
8376
8377   $darea4 = Gtk2::DrawingArea->new;
8378   $darea4->modify_bg ('normal', Gtk2::Gdk::Color->parse ($color_cdplayer_shadow_motion) ); # set the color
8379
8380   my $button_cdplayer_color4 = Gtk2::Button->new;
8381   $button_cdplayer_color4->add ($darea4);
8382   $button_cdplayer_color4->set_size_request (@def_size); # set a minimum size
8383   $button_cdplayer_color4->signal_connect (clicked => \&change_color_cdplayer_bg, "shadow_motion" );
8384   $table_cdplayer_color->attach ($button_cdplayer_color4, 1, 2, 7, 8, 'shrink', 'fill', 0, 0);
8385
8386
8387   my $label_cdplayer_color5 = Gtk2::Label->new("Buttons (arrows): ");
8388   $label_cdplayer_color5->set_alignment( 1.0, 0.5 );
8389   $table_cdplayer_color->attach ($label_cdplayer_color5, 0, 1, 8, 9, 'fill', 'fill', 0, 0);
8390
8391   $darea5 = Gtk2::DrawingArea->new;
8392   $darea5->modify_bg ('normal', Gtk2::Gdk::Color->parse ($color_cdplayer_arrows_motion) ); # set the color
8393
8394   my $button_cdplayer_color5 = Gtk2::Button->new;
8395   $button_cdplayer_color5->add ($darea5);
8396   $button_cdplayer_color5->set_size_request (@def_size); # set a minimum size
8397   $button_cdplayer_color5->signal_connect (clicked => \&change_color_cdplayer_bg, "arrows_motion" );
8398   $table_cdplayer_color->attach ($button_cdplayer_color5, 1, 2, 8, 9, 'shrink', 'fill', 0, 0);
8399
8400
8401   #-------- Digits --------#
8402
8403   my $hsep3 = Gtk2::HSeparator->new;
8404   $table_cdplayer_color->attach ($hsep3, 0, 2, 9, 10, ['fill','expand'], 'fill', 0, 0);
8405   my $label_dig = Gtk2::Label->new("Digits");
8406   $table_cdplayer_color->attach ($label_dig, 0, 2, 10, 11, 'fill', 'fill', 0, 0);
8407
8408   my $label_cdplayer_color6 = Gtk2::Label->new("Current time: ");
8409   $label_cdplayer_color6->set_alignment( 1.0, 0.5 );
8410   $table_cdplayer_color->attach ($label_cdplayer_color6, 0, 1, 11, 12, 'fill', 'fill', 0, 0);
8411
8412   $darea6 = Gtk2::DrawingArea->new;
8413   $darea6->modify_bg ('normal', Gtk2::Gdk::Color->parse ($color_cdplayer_digit) ); # set the color
8414
8415   my $button_cdplayer_color6 = Gtk2::Button->new;
8416   $button_cdplayer_color6->add ($darea6);
8417   $button_cdplayer_color6->set_size_request (@def_size); # set a minimum size
8418   $button_cdplayer_color6->signal_connect (clicked => \&change_color_cdplayer_bg, "digit" );
8419   $table_cdplayer_color->attach ($button_cdplayer_color6, 1, 2, 11, 12, 'shrink', 'fill', 0, 0);
8420
8421
8422   my $label_cdplayer_color7 = Gtk2::Label->new("Remaining time: ");
8423   $label_cdplayer_color7->set_alignment( 1.0, 0.5 );
8424   $table_cdplayer_color->attach ($label_cdplayer_color7, 0, 1, 12, 13, 'fill', 'fill', 0, 0);
8425
8426   $darea7 = Gtk2::DrawingArea->new;
8427   $darea7->modify_bg ('normal', Gtk2::Gdk::Color->parse ($color_cdplayer_digit_remaining) ); # set the color
8428
8429   my $button_cdplayer_color7 = Gtk2::Button->new;
8430   $button_cdplayer_color7->add ($darea7);
8431   $button_cdplayer_color7->set_size_request (@def_size); # set a minimum size
8432   $button_cdplayer_color7->signal_connect (clicked => \&change_color_cdplayer_bg, "digit_remaining" );
8433   $table_cdplayer_color->attach ($button_cdplayer_color7, 1, 2, 12, 13, 'shrink', 'fill', 0, 0);
8434
8435
8436   #------- Players -------#
8437   my $table_cdplayer_players = new Gtk2::Table (6, 3, 0);
8438   $table_cdplayer_players->set_border_width (4);
8439   $table_cdplayer_players->set_row_spacings(4);
8440   $table_cdplayer_players->set_col_spacings(2);
8441   $frame_cdplayer_players->add( $table_cdplayer_players );
8442
8443   my $label_cdplayer_players0 = Gtk2::Label->new("player");
8444   $label_cdplayer_players0->set_alignment( 0.5, 0.5 );
8445   $table_cdplayer_players->attach ($label_cdplayer_players0, 1, 2, 0, 1, 'fill', 'fill', 0, 0);
8446
8447   my $label_cdplayer_players01 = Gtk2::Label->new("device type");
8448   $label_cdplayer_players01->set_alignment( 0.5, 0.5 );
8449   $table_cdplayer_players->attach ($label_cdplayer_players01, 3, 4, 0, 1, 'fill', 'fill', 0, 0);
8450
8451   my $label_cdplayer_players02 = Gtk2::Label->new("device path");
8452   $label_cdplayer_players02->set_alignment( 0.5, 0.5 );
8453   $table_cdplayer_players->attach ($label_cdplayer_players02, 3, 4, 2, 3, 'fill', 'fill', 0, 0);
8454
8455   my $label_cdplayer_players1 = Gtk2::Label->new("mp3: ");
8456   $label_cdplayer_players1->set_alignment( 1.0, 0.5 );
8457   $table_cdplayer_players->attach ($label_cdplayer_players1, 0, 1, 1, 2, 'fill', 'fill', 0, 0);
8458
8459   my $label_cdplayer_players2 = Gtk2::Label->new("mp4: ");
8460   $label_cdplayer_players2->set_alignment( 1.0, 0.5 );
8461   $table_cdplayer_players->attach ($label_cdplayer_players2, 0, 1, 2, 3, 'fill', 'fill', 0, 0);
8462
8463   my $label_cdplayer_players3 = Gtk2::Label->new("mpc: ");
8464   $label_cdplayer_players3->set_alignment( 1.0, 0.5 );
8465   $table_cdplayer_players->attach ($label_cdplayer_players3, 0, 1, 3, 4, 'fill', 'fill', 0, 0);
8466
8467   my $label_cdplayer_players4 = Gtk2::Label->new("ogg: ");
8468   $label_cdplayer_players4->set_alignment( 1.0, 0.5 );
8469   $table_cdplayer_players->attach ($label_cdplayer_players4, 0, 1, 4, 5, 'fill', 'fill', 0, 0);
8470
8471   my $label_cdplayer_players5 = Gtk2::Label->new("ape: ");
8472   $label_cdplayer_players5->set_alignment( 1.0, 0.5 );
8473   $table_cdplayer_players->attach ($label_cdplayer_players5, 0, 1, 5, 6, 'fill', 'fill', 0, 0);
8474
8475   my $label_cdplayer_players6 = Gtk2::Label->new("flac: ");
8476   $label_cdplayer_players6->set_alignment( 1.0, 0.5 );
8477   $table_cdplayer_players->attach ($label_cdplayer_players6, 0, 1, 6, 7, 'fill', 'fill', 0, 0);
8478
8479   my $label_cdplayer_players7 = Gtk2::Label->new("wav: ");
8480   $label_cdplayer_players7->set_alignment( 1.0, 0.5 );
8481   $table_cdplayer_players->attach ($label_cdplayer_players7, 0, 1, 7, 8, 'fill', 'fill', 0, 0);
8482
8483
8484   #---mp3---#
8485
8486   my $ComboBox_mp3_player = Gtk2::ComboBox->new_text;  # "mpg321", "mpg123", "madplay", "mplayer"
8487   foreach (sort keys %all_mp3_player) { $ComboBox_mp3_player->append_text ($_); }
8488   my $pos = 0;
8489   foreach my $key (sort keys %all_mp3_player) { if ($key eq $player_mp3) {$ComboBox_mp3_player->set_active($pos); last;} $pos++; }
8490   $ComboBox_mp3_player->set_size_request( 90, -1 );
8491   $ComboBox_mp3_player->signal_connect("changed", \&player_choice, \$player_mp3);
8492   $table_cdplayer_players->attach ($ComboBox_mp3_player, 1, 2, 1, 2, ['fill','expand'], 'fill', 0, 0);
8493   $ComboBox_mp3_player->show;
8494
8495   sub player_choice {
8496      my ($ComboBox_copy, $ref_player) = @_;
8497      my $entry = $ComboBox_copy->get_active_text;
8498
8499      #  See 'man perlref', 'man perlop' and 'man perlobj'.
8500      #  "\" operator for taking a reference;  "$" operator for taking a dereference to scalar variable.
8501      # "$player" is a scalar variable that references to a variable.
8502      # "\$player" typically creates another reference to a variable, because there's already a reference to the variable in the symbol table.
8503      # "$$player" is the value that the reference "\$player" points to.
8504
8505      $$ref_player = $entry;
8506      #print "\nComboBox_copy = $ComboBox_copy ;; \nref_player = $ref_player ;; ref_palyer value = $$ref_player  \n";
8507   }
8508
8509   #---------#
8510   my @device_type_array = ( "arts", "oss", "alsa", "esd", "jack", "nas", "sun" );
8511
8512   my $ComboBoxEntry_device_type = Gtk2::ComboBoxEntry->new_text;
8513   ComboBox_fill_with_array(\$ComboBoxEntry_device_type,\@device_type_array);
8514   $ComboBoxEntry_device_type->child->set_text( $device_type ); #standard value
8515   $ComboBoxEntry_device_type->set_size_request( 90, -1 );
8516   $ComboBoxEntry_device_type->child->set_editable ($true);
8517   $ComboBoxEntry_device_type->signal_connect("changed", \&device_choice, \$device_type);
8518   $table_cdplayer_players->attach ($ComboBoxEntry_device_type, 3, 4, 1, 2, ['fill','expand'], 'fill', 0, 0);
8519   #$ComboBoxEntry_device_type->show;
8520
8521   sub device_choice {
8522      my ($combo_copy, $ref_device) = @_;
8523      my $entry=$combo_copy->child->get_text;
8524      if ( $entry eq "" ){ return; } # return if empty
8525      $$ref_device = $entry;
8526   }
8527
8528   my @device_path_array = ( "/dev/audio", "/dev/sound/adsp", "/dev/dsp" );
8529
8530   my $ComboBoxEntry_device_path = Gtk2::ComboBoxEntry->new_text;
8531   ComboBox_fill_with_array(\$ComboBoxEntry_device_path,\@device_path_array);
8532   $ComboBoxEntry_device_path->child->set_text( $device_path ); #standard value
8533   $ComboBoxEntry_device_path->set_size_request( 140, -1 );
8534   $ComboBoxEntry_device_path->child->set_editable ($true);
8535   $ComboBoxEntry_device_path->signal_connect("changed", \&device_choice, \$device_path);
8536   $table_cdplayer_players->attach ($ComboBoxEntry_device_path, 3, 4, 3, 4, ['fill','expand'], 'fill', 0, 0);
8537
8538   #---mp4---#
8539   my $ComboBox_mp4_player = Gtk2::ComboBox->new_text;  # "mplayer"
8540   foreach (sort keys %all_mp4_player) { $ComboBox_mp4_player->append_text ($_); }
8541   $pos = 0;
8542   foreach my $key (sort keys %all_mp4_player) { if ($key eq $player_mp4) {$ComboBox_mp4_player->set_active($pos); last;} $pos++; }
8543   $ComboBox_mp4_player->set_size_request( 90, -1 );
8544   $ComboBox_mp4_player->signal_connect("changed", \&player_choice, \$player_mp4);
8545   $table_cdplayer_players->attach ($ComboBox_mp4_player, 1, 2, 2, 3, ['fill','expand'], 'fill', 0, 0);
8546   $ComboBox_mp4_player->show;
8547
8548   #---mpc---#
8549   my $ComboBox_mpc_player = Gtk2::ComboBox->new_text;  # "mpcdec", "mplayer"
8550   foreach (sort keys %all_mpc_player) { $ComboBox_mpc_player->append_text ($_); }
8551   $pos = 0;
8552   foreach my $key (sort keys %all_mpc_player) { if ($key eq $player_mpc) {$ComboBox_mpc_player->set_active($pos); last;} $pos++; }
8553   $ComboBox_mpc_player->set_size_request( 90, -1 );
8554   $ComboBox_mpc_player->signal_connect("changed", \&player_choice, \$player_mpc);
8555   $table_cdplayer_players->attach ($ComboBox_mpc_player, 1, 2, 3, 4, ['fill','expand'], 'fill', 0, 0);
8556   $ComboBox_mpc_player->show;
8557
8558   #---ogg---#
8559   my $ComboBox_ogg_player = Gtk2::ComboBox->new_text;  # "ogg123", "mplayer"
8560   foreach (sort keys %all_ogg_player) { $ComboBox_ogg_player->append_text ($_); }
8561   $pos = 0;
8562   foreach my $key (sort keys %all_ogg_player) { if ($key eq $player_ogg) {$ComboBox_ogg_player->set_active($pos); last;} $pos++; }
8563   $ComboBox_ogg_player->set_size_request( 90, -1 );
8564   $ComboBox_ogg_player->signal_connect("changed", \&player_choice, \$player_ogg);
8565   $table_cdplayer_players->attach ($ComboBox_ogg_player, 1, 2, 4, 5, ['fill','expand'], 'fill', 0, 0);
8566   $ComboBox_ogg_player->show;
8567
8568   #---ape---#
8569   my $ComboBox_ape_player = Gtk2::ComboBox->new_text;  # "mac + play", "mplayer"
8570   foreach (sort keys %all_ape_player) { $ComboBox_ape_player->append_text ($_); }
8571   $pos = 0;
8572   foreach my $key (sort keys %all_ape_player) { if ($key eq $player_ape) {$ComboBox_ape_player->set_active($pos); last;} $pos++; }
8573   $ComboBox_ape_player->set_size_request( 90, -1 );
8574   $ComboBox_ape_player->signal_connect("changed", \&player_choice, \$player_ape);
8575   $table_cdplayer_players->attach ($ComboBox_ape_player, 1, 2, 5, 6, ['fill','expand'], 'fill', 0, 0);
8576   $ComboBox_ape_player->show;
8577
8578   #---flac---#
8579   my $ComboBox_flac_player = Gtk2::ComboBox->new_text;  # "flac123", "mplayer"
8580   foreach (sort keys %all_flac_player) { $ComboBox_flac_player->append_text ($_); }
8581   $pos = 0;
8582   foreach my $key (sort keys %all_flac_player) { if ($key eq $player_flac) {$ComboBox_flac_player->set_active($pos); last;} $pos++; }
8583   $ComboBox_flac_player->set_size_request( 90, -1 );
8584   $ComboBox_flac_player->signal_connect("changed", \&player_choice, \$player_flac);
8585   $table_cdplayer_players->attach ($ComboBox_flac_player, 1, 2, 6, 7, ['fill','expand'], 'fill', 0, 0);
8586   $ComboBox_flac_player->show;
8587
8588   #---wav---#
8589   my $ComboBox_wav_player = Gtk2::ComboBox->new_text;  # "mplayer"
8590   foreach (sort keys %all_wav_player) { $ComboBox_wav_player->append_text ($_); }
8591   $pos = 0;
8592   foreach my $key (sort keys %all_wav_player) { if ($key eq $player_wav) {$ComboBox_wav_player->set_active($pos); last;} $pos++; }
8593   $ComboBox_wav_player->set_size_request( 90, -1 );
8594   $ComboBox_wav_player->signal_connect("changed", \&player_choice, \$player_wav);
8595   $table_cdplayer_players->attach ($ComboBox_wav_player, 1, 2, 7, 8, ['fill','expand'], 'fill', 0, 0);
8596   $ComboBox_wav_player->show;
8597
8598   #----------- play_with_filter -----------#
8599   my @all_filter = ( $langs{name429} , # = 'Play Tracks Without Any Filters';
8600                      $langs{name430} , # = 'Play Tracks From Different Albums';
8601                      $langs{name431} , # = 'Play Tracks From Different Artists';
8602                      $langs{name432} , # = 'Play Tracks From Different Genres';
8603                      $langs{name433} , # = 'Play Tracks From Different Years';
8604		    );
8605
8606   $ComboBox_player_with_filter = Gtk2::ComboBox->new_text;  # filter can be albums, artists, ...
8607   foreach (@all_filter) { $ComboBox_player_with_filter->append_text ($_); }
8608   $ComboBox_player_with_filter->set_active($use_filter);  # default value
8609   $ComboBox_player_with_filter->set_size_request( 90, -1 );
8610   $ComboBox_player_with_filter->signal_connect("changed", \&change_filter);
8611   $table_cdplayer_players->attach ($ComboBox_player_with_filter, 0, 4, 8, 9, ['fill','expand'], 'fill', 0, 0);
8612   $tooltips->set_tip( $ComboBox_player_with_filter, $langs{tip429} ) if $show_all_tooltips;  # "Play choosing only tracks that satisfies established filters...";
8613   $ComboBox_player_with_filter->show;
8614
8615   sub change_filter {  # see 'sub choose_filter'
8616      $use_filter = $ComboBox_player_with_filter->get_active; # $use_filter is a number
8617
8618      #@rows_already_played = (); # if change the filter, then reset playlist of rows_already_played.
8619   }
8620
8621   #---------------- Columns ---------------#
8622   my $table_cdplayer_columns = new Gtk2::Table (10, 3, 0);
8623   $table_cdplayer_columns->set_border_width (4);
8624   $table_cdplayer_columns->set_row_spacings(4);
8625   $table_cdplayer_columns->set_col_spacings(2);
8626   $frame_cdplayer_columns->add( $table_cdplayer_columns );
8627
8628
8629   my $check_button_file = Gtk2::CheckButton->new ("Show File Number");
8630   $check_button_file->set_active($column_show_file);
8631   $check_button_file->signal_connect( "clicked", sub{
8632      $column_show_file = $check_button_file->get_active;
8633      add_columns( show_play => $true);
8634      show_scrolling_text();
8635   });
8636   $table_cdplayer_columns->attach ($check_button_file, 0, 1, 0, 1, 'fill', 'fill', 0, 0);
8637
8638
8639   my $check_button_artist = Gtk2::CheckButton->new ("Show Artist");
8640   $check_button_artist->set_active($column_show_artist);
8641   $check_button_artist->signal_connect( "clicked", sub{
8642      $column_show_artist = $check_button_artist->get_active;
8643      add_columns( show_play => $true);
8644      show_scrolling_text();
8645   });
8646   $table_cdplayer_columns->attach ($check_button_artist, 0, 1, 1, 2, 'fill', 'fill', 0, 0);
8647
8648   my $check_button_album = Gtk2::CheckButton->new ("Show Album");
8649   $check_button_album->set_active($column_show_album);
8650   $check_button_album->signal_connect( "clicked", sub{
8651      $column_show_album = $check_button_album->get_active;
8652      add_columns( show_play => $true);
8653      show_scrolling_text();
8654   });
8655   $table_cdplayer_columns->attach ($check_button_album, 0, 1, 2, 3, 'fill', 'fill', 0, 0);
8656
8657   my $check_button_track = Gtk2::CheckButton->new ("Show Track");
8658   $check_button_track->set_active($column_show_track);
8659   $check_button_track->signal_connect( "clicked", sub{
8660      $column_show_track = $check_button_track->get_active;
8661      add_columns( show_play => $true);
8662      show_scrolling_text();
8663   });
8664   $table_cdplayer_columns->attach ($check_button_track, 0, 1, 3, 4, 'fill', 'fill', 0, 0);
8665
8666   my $check_button_bitrate = Gtk2::CheckButton->new ("Show Bitrate");
8667   $check_button_bitrate->set_active($column_show_bitrate);
8668   $check_button_bitrate->signal_connect( "clicked", sub{
8669      $column_show_bitrate = $check_button_bitrate->get_active;
8670      add_columns( show_play => $true);
8671      show_scrolling_text();
8672   });
8673   $table_cdplayer_columns->attach ($check_button_bitrate, 0, 1, 4, 5, 'fill', 'fill', 0, 0);
8674
8675   my $check_button_year = Gtk2::CheckButton->new ("Show Year");
8676   $check_button_year->set_active($column_show_year);
8677   $check_button_year->signal_connect( "clicked", sub{
8678      $column_show_year = $check_button_year->get_active;
8679      add_columns( show_play => $true);
8680   });
8681   $table_cdplayer_columns->attach ($check_button_year, 0, 1, 5, 6, 'fill', 'fill', 0, 0);
8682
8683
8684   my $check_button_frequency = Gtk2::CheckButton->new ("Show Frequency");
8685   $check_button_frequency->set_active($column_show_frequency);
8686   $check_button_frequency->signal_connect( "clicked", sub{
8687      $column_show_frequency = $check_button_frequency->get_active;
8688      add_columns( show_play => $true);
8689   });
8690   $table_cdplayer_columns->attach ($check_button_frequency, 0, 1, 6, 7, 'fill', 'fill', 0, 0);
8691
8692   my $check_button_filepath = Gtk2::CheckButton->new ("Show Filepath");
8693   $check_button_filepath->set_active($column_show_filepath);
8694   $check_button_filepath->signal_connect( "clicked", sub{
8695      $column_show_filepath = $check_button_filepath->get_active;
8696      add_columns( show_play => $true);
8697   });
8698   $table_cdplayer_columns->attach ($check_button_filepath, 0, 1, 7, 8, 'fill', 'fill', 0, 0);
8699
8700
8701   my $check_button_filename = Gtk2::CheckButton->new ("Show Filename");
8702   $check_button_filename->set_active($column_show_filename);
8703   $check_button_filename->signal_connect( "clicked", sub{
8704      $column_show_filename = $check_button_filename->get_active;
8705      add_columns( show_play => $true);
8706   });
8707   $table_cdplayer_columns->attach ($check_button_filename, 0, 1, 8, 9, 'fill', 'fill', 0, 0);
8708
8709
8710   my $check_button_extension = Gtk2::CheckButton->new ("Show Extension");
8711   $check_button_extension->set_active($column_show_extension);
8712   $check_button_extension->signal_connect( "clicked", sub{
8713      $column_show_extension = $check_button_extension->get_active;
8714      add_columns( show_play => $true);
8715   });
8716   $table_cdplayer_columns->attach ($check_button_extension, 0, 1, 9, 10, 'fill', 'fill', 0, 0);
8717
8718   my $check_button_play_count = Gtk2::CheckButton->new ("Show Play Count");
8719   $check_button_play_count->set_active($column_show_play_count);
8720   $check_button_play_count->signal_connect( "clicked", sub{
8721      $column_show_play_count = $check_button_play_count->get_active;
8722      add_columns_album_artist( column_show_play_count => $column_show_play_count);
8723   });
8724   $table_cdplayer_columns->attach ($check_button_play_count, 0, 1, 10, 11, 'fill', 'fill', 0, 0);
8725
8726   #---------------- Skins ---------------#
8727   my $table_cdplayer_skins = new Gtk2::Table (6, 3, 0);
8728   $table_cdplayer_skins->set_border_width (4);
8729   $table_cdplayer_skins->set_row_spacings(4);
8730   $table_cdplayer_skins->set_col_spacings(4);
8731   $frame_cdplayer_skins->add( $table_cdplayer_skins );
8732
8733   my $label_st = Gtk2::Label->new("Scrolling Text");
8734   $table_cdplayer_skins->attach ($label_st, 0, 2, 0, 1, 'fill', 'fill', 0, 0);
8735
8736   ###---###
8737   my $label_scrolling_type = new Gtk2::Label( "Type:" );
8738   $label_scrolling_type->set_alignment( 1.0, 0.5 );
8739   $table_cdplayer_skins->attach ($label_scrolling_type, 0, 1, 1, 2, 'fill', 'fill', 0, 0);
8740
8741   my @all_scrolling = ( "horinzontal1", "horinzontal2", "horinzontal3", "vertical", "random" );
8742
8743   my $ComboBox_scrolling = Gtk2::ComboBox->new_text;
8744   ComboBox_fill_with_array(\$ComboBox_scrolling,\@all_scrolling);
8745   ComboBox_select_this_text(\$ComboBox_scrolling,\@all_scrolling,\$scrolling_type);  #standard value
8746   $ComboBox_scrolling->set_size_request( 120, -1 );
8747   $ComboBox_scrolling->signal_connect("changed", \&scrolling_choice, $ComboBox_scrolling);
8748   $table_cdplayer_skins->attach ($ComboBox_scrolling, 1, 2, 1, 2, ['fill','expand'], 'fill', 0, 0);
8749   #$ComboBox_scrolling->show;
8750
8751   sub scrolling_choice {
8752        my ($self, $ComboBox_copy) = @_;
8753	$scrolling_type = $ComboBox_copy->get_active_text;
8754	show_scrolling_text();
8755   }
8756   ###---###
8757
8758   my $label_sts = new Gtk2::Label( "Interval:" );
8759   $label_sts->set_alignment( 1.0, 0.5 );
8760   $table_cdplayer_skins->attach ($label_sts, 0, 1, 2, 3, 'fill', 'fill', 0, 0);
8761
8762   my $adj = new Gtk2::Adjustment( $scrolling_text_speed, 10, 2000, 5, 0, 0 );
8763   my $spinner_text = new Gtk2::SpinButton( $adj, 1.2, 0 );
8764   $spinner_text->set_editable($true);
8765   $spinner_text->set_size_request( 120, -1 );
8766   $spinner_text->set_update_policy( 'if_valid' );
8767   $spinner_text->set_wrap( $false );
8768   $tooltips->set_tip( $spinner_text, $langs{tip407} ) if $show_all_tooltips;
8769   $spinner_text->signal_connect( "value-changed", sub{
8770      $scrolling_text_speed = $spinner_text->get_value_as_int;
8771      begin_scroll_loop();
8772      #print "scrolling_text_speed = $scrolling_text_speed\n";
8773   });
8774   $table_cdplayer_skins->attach ($spinner_text, 1, 2, 2, 3, ['fill','expand'], 'fill', 0, 0);
8775
8776
8777   my $label_step = new Gtk2::Label( "Step:" );
8778   $label_step->set_alignment( 1.0, 0.5 );
8779   $table_cdplayer_skins->attach ($label_step, 0, 1, 3, 4, 'fill', 'fill', 0, 0);
8780
8781   $adj = new Gtk2::Adjustment( $scrolling_text_incremental, 1, 20, 1, 0, 0 );
8782   my $spinner_step = new Gtk2::SpinButton( $adj, 0.2, 0 );
8783   $spinner_step->set_editable($true);
8784   $spinner_step->set_size_request( 120, -1 );
8785   $spinner_step->set_update_policy( 'if_valid' );
8786   $spinner_step->set_wrap( $false );
8787   $tooltips->set_tip( $spinner_step, $langs{tip408} ) if $show_all_tooltips;
8788   $spinner_step->signal_connect( "value-changed", sub{
8789      $scrolling_text_incremental = $spinner_step->get_value_as_int;
8790      #print "scrolling_text_incremental = $scrolling_text_incremental\n";
8791   });
8792   $table_cdplayer_skins->attach ($spinner_step, 1, 2, 3, 4, 'fill', 'fill', 0, 0);
8793
8794
8795   my $check_button_orientation = Gtk2::CheckButton->new ("Scrolling text orientation");
8796   $tooltips->set_tip( $check_button_orientation, $langs{tip409} ) if $show_all_tooltips;
8797   $check_button_orientation->set_active( $scrolling_text_orientation == 1 ? $true : $false );
8798   $check_button_orientation->signal_connect( "clicked", sub{
8799      $scrolling_text_orientation = $check_button_orientation->get_active ? +1 : -1;
8800   });
8801   $table_cdplayer_skins->attach ($check_button_orientation, 0, 2, 4, 5, 'fill', 'fill', 0, 0);
8802
8803   my $hsepSkin = Gtk2::HSeparator->new;
8804   $table_cdplayer_skins->attach ($hsepSkin, 0, 2, 5, 6, ['fill','expand'], 'fill', 0, 0);
8805
8806
8807   my $check_button_skin = Gtk2::CheckButton->new ("Show skin");
8808   $tooltips->set_tip( $check_button_skin, $langs{tip410} ) if $show_all_tooltips;
8809   $check_button_skin->set_active($show_cdplayer_skin);
8810   $check_button_skin->signal_connect( "clicked", sub{
8811      $show_cdplayer_skin = $check_button_skin->get_active;
8812      sensitive_bg_color();
8813   });
8814   $table_cdplayer_skins->attach ($check_button_skin, 0, 1, 6, 7, 'fill', 'fill', 0, 0);
8815
8816   my $check_button_border = Gtk2::CheckButton->new ("Show border");
8817   $check_button_border->set_active($show_cdplayer_border);
8818   $check_button_border->signal_connect( "clicked", sub{
8819      $show_cdplayer_border = $check_button_border->get_active;
8820   });
8821   $table_cdplayer_skins->attach ($check_button_border, 0, 1, 7, 8, 'fill', 'fill', 0, 0);
8822
8823   my $check_button_keep_above = Gtk2::CheckButton->new ("Keep above");
8824   $tooltips->set_tip( $check_button_keep_above, $langs{tip411} ) if $show_all_tooltips;
8825   $check_button_keep_above->set_active($show_cdplayer_keep_above);
8826   $check_button_keep_above->signal_connect( "clicked", sub{
8827      $show_cdplayer_keep_above = $check_button_keep_above->get_active;
8828   });
8829   $table_cdplayer_skins->attach ($check_button_keep_above, 0, 1, 8, 9, 'fill', 'fill', 0, 0);
8830
8831   #---------------- hbox ----------------#
8832
8833   my $hbox2 = Gtk2::HBox->new ($false, 0);
8834   $hbox2->set_border_width (2);
8835   $vbox_cdplayer_color->pack_start ($hbox2, $false, $false, 0);
8836
8837   my $label_empty2 = Gtk2::Label->new(" ");
8838   $hbox2->pack_start ($label_empty2, $true, $false, 0);
8839
8840   my $button = Gtk2::Button->new;
8841   $button->add( DrawIcons('gtk-close','button') );
8842   $tooltips->set_tip( $button, $langs{tip406} ) if $show_all_tooltips;
8843   $button->signal_connect( "clicked", \&Quit_window_cdplayer_color );
8844   $hbox2->pack_start ($button, $false, $false, 0);
8845
8846   my $label_empty3 = Gtk2::Label->new(" ");
8847   $hbox2->pack_start ($label_empty3, $true, $false, 0);
8848}
8849
8850sub sensitive_bg_color{
8851   if ( defined $window_cdplayer_color ){
8852      $button_cdplayer_color->set_sensitive($true);
8853      if (defined $pixmap_mask){ # have skin_xTunes
8854         $button_cdplayer_color->set_sensitive($false);
8855      }
8856   }
8857}
8858
8859sub cdplayer_color_selection {
8860
8861   if ( not defined($window_cdplayer_color) ) {
8862      make_window_cdplayer_color();
8863      $window_cdplayer_color->show_all;
8864
8865      #---- $notebook->set_current_page  ----#
8866
8867      $notebook_cdplayer->set_current_page ($notebook_cd_player_num);
8868      #$notebook_cd_player_num = $notebook->get_current_page;
8869      #$notebook->set_current_page ($notebook_cd_player_num); # see this command on the final
8870      #print "\ncdplayer_color_selection -->1 \$notebook_cd_player_num = $notebook_cd_player_num\n";
8871   }
8872   else {
8873      Quit_window_cdplayer_color();
8874  }
8875}
8876
8877sub Quit_window_cdplayer_color {
8878      $notebook_cd_player_num = $notebook_cdplayer->get_current_page;
8879      #print "\ncdplayer_color_selection -->2 \$notebook_cd_player_num = $notebook_cd_player_num\n";
8880
8881      $window_cdplayer_color->destroy;
8882      $window_cdplayer_color = undef;
8883}
8884
8885sub change_color_cdplayer_bg {
8886  my ( $self, $region ) = @_;
8887  my $color;
8888
8889  # repair the standard cancel label
8890  my @items = ( { stock_id => "gtk-cancel", label => "_Cancel" } );
8891  Gtk2::Stock->add (@items);  # Register our stock items
8892
8893  my $dialog = Gtk2::ColorSelectionDialog->new ("Change the color");
8894  $dialog->set_transient_for ($window);
8895  my $colorsel = $dialog->colorsel;
8896
8897  my $previous_color;
8898  # $color_bg_cdplay = '#E6EDBD'; $color_cdplayer_shadow = '#6E95EF'; $color_cdplayer_arrows = '#000000';
8899  if    ($region eq 'display'){  $previous_color = Gtk2::Gdk::Color->parse ($color_bg_cdplay);        }
8900  elsif ($region eq 'shadow' ){  $previous_color = Gtk2::Gdk::Color->parse ($color_cdplayer_shadow);  }
8901  elsif ($region eq 'arrows' ){  $previous_color = Gtk2::Gdk::Color->parse ($color_cdplayer_arrows);  }
8902  elsif ($region eq 'shadow_motion' ){  $previous_color = Gtk2::Gdk::Color->parse ($color_cdplayer_shadow_motion);  }
8903  elsif ($region eq 'arrows_motion' ){  $previous_color = Gtk2::Gdk::Color->parse ($color_cdplayer_arrows_motion);  }
8904  elsif ($region eq 'digit'         ){  $previous_color = Gtk2::Gdk::Color->parse ($color_cdplayer_digit);          }
8905  elsif ($region eq 'digit_remaining' ){  $previous_color = Gtk2::Gdk::Color->parse ($color_cdplayer_digit_remaining);  }
8906
8907  $colorsel->set_previous_color ($previous_color);
8908  $colorsel->set_current_color  ($previous_color);
8909  $colorsel->set_has_palette ($true);
8910  $colorsel->set_has_opacity_control ($false);
8911
8912  my $response = $dialog->run;
8913
8914  if ($response eq 'ok') {
8915      my $color_gdk = $colorsel->get_current_color; # $color = Gtk2::Gdk::Color
8916      my ($red, $green, $blue) = ( $color_gdk->red, $color_gdk->green, $color_gdk->blue );
8917
8918      # uc: Returns an uppercased version of EXPR.
8919      # %x   an unsigned integer, in hexadecimal ; l: long
8920      $red   = uc( sprintf( "%02lx", $red/256 ) );  #print "red = $red\n";
8921      $green = uc( sprintf( "%02lx", $green/256 ) );
8922      $blue  = uc( sprintf( "%02lx", $blue/256 ) );
8923
8924      $color = "#${red}${green}${blue}"; # print "color = $color\n";  # like #E6E7E6 ; HTML-style hexadecimal
8925
8926      if ($region eq 'display'){
8927         configure_event_cdplayer( $da, undef, ($color, undef) ); # change display color
8928         $darea1->modify_bg ( 'normal', $color_gdk );
8929      }
8930      elsif ($region eq 'shadow'){
8931         configure_event_cdplayer( $da, undef, (undef, $color) ); # change buttons color (shadow)
8932         $darea2->modify_bg ( 'normal', $color_gdk );
8933      }
8934      elsif ($region eq 'arrows'){
8935         configure_event_cdplayer( $da, undef, (undef, undef, $color) ); # change buttons color (arrows)
8936         $darea3->modify_bg ( 'normal', $color_gdk );
8937      }
8938      elsif ($region eq 'shadow_motion'){
8939         configure_event_cdplayer( $da, undef, (undef, undef, undef, $color) ); # change buttons color (shadow_motion)
8940         $darea4->modify_bg ( 'normal', $color_gdk );
8941      }
8942      elsif ($region eq 'arrows_motion'){
8943         configure_event_cdplayer( $da, undef, (undef, undef, undef, undef, $color) ); # change buttons color (arrows_motion)
8944         $darea5->modify_bg ( 'normal', $color_gdk );
8945      }
8946      elsif ($region eq 'digit'){
8947         configure_event_cdplayer( $da, undef, (undef, undef, undef, undef, undef, $color) ); # change buttons color (digit)
8948         $darea6->modify_bg ( 'normal', $color_gdk );
8949      }
8950      elsif ($region eq 'digit_remaining'){
8951         configure_event_cdplayer( $da, undef, (undef, undef, undef, undef, undef, undef, $color) ); # change buttons color (digit_remaining)
8952         $darea7->modify_bg ( 'normal', $color_gdk );
8953      }
8954      # refresh the whole area $da
8955      $da->queue_draw_area (0, 0, $da->allocation->width, $da->allocation->height);
8956  }
8957  $dialog->destroy;
8958  return $color;
8959}
8960
8961#--------------------- Final -------------------------#
8962#----------- CD player Color Selection ---------------#
8963my ($click_x, $click_y) = (0,0);
8964
8965sub button_press_event_cdplayer {
8966  my ($widget, $event, $data) = @_;    # $widget = $da = Gtk2::DrawingArea
8967
8968  my (undef, $x, $y, $state) = $event->window->get_pointer;
8969  if ( $cdplayer eq "Audio::CD" ){ $audiocd = Audio::CD->init("$audiodevice_path"); }
8970
8971  ($click_x, $click_y) = ($x, $y);
8972
8973  $go_back = $false;
8974  $go_forward = $false;
8975
8976  my $width_r  = $buttons_w - 0;  # 14
8977  my $height_r = $buttons_h - 2;  # height of buttons
8978
8979  # Press mouse button 2 or 3 on cdplayer display to hide the main window
8980  if ($event->button >= 2) {
8981     show_only_cd_display();
8982     sensitive_bg_color(); # cd_player bg color change
8983  }
8984
8985  if ($event->button == 1) {
8986     my $height = $widget->allocation->height;
8987     my $width = $widget->allocation->width ;
8988
8989     # stop button
8990     my $xdest_but = $xdest_buttons - 1*$buttons_w/2 + 0*$delta;
8991     if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
8992        draw_buttons(Press_Stop => $true, Draw_Stop => $true);
8993	$button_press_stop = $true;
8994        #print "vc clicou em stop: x = $x, y = $y ;; height = $height ;; width = $width ;; dcentery = $dcentery\n";
8995	stop_playing_music();
8996	show_time();
8997	$pause = $false;
8998	return $true;
8999     }
9000     # (play || pause) buttons display
9001     $xdest_but = $xdest_buttons + 1*$buttons_w/2 + 1*$delta;
9002     if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9003        draw_buttons(Press_Play => $true, Draw_Play => $true); # change button color
9004	$button_press_play = $true;
9005	if ( $playing_music eq $false or $pause eq $true){
9006           # Set volume level to play audio cd.
9007           change_volume_level( undef, $audio_cd_volume ); # 0 - 100
9008
9009	   if ( $pause eq $false and $playing_music eq $false ){ play_selection();}
9010
9011	   if ( $pause eq $true){
9012
9013	      if ($files_info[$selected_row]{extension_input} ne 'cda'){
9014	         if(defined $pid_player){kill 18,$pid_player}; # to resume; See "Signals" in perlipc
9015	         $pause = $false;
9016	         return $true;
9017              }
9018
9019	      if ( $cdplayer eq "cdcd" ){
9020		 my @cmd = ( $cdcd_path , '-d', $audiodevice_path, 'resume' );
9021                 exec_cmd_system2(@cmd);
9022              }
9023	      elsif ( $cdplayer eq "gnormalize::cdplay" ){
9024	         gnormalize::cdplay::resume_cdrom();
9025	      }
9026	      elsif ( $cdplayer eq "Audio::CD" ){
9027	         $audiocd->resume;
9028	      }
9029	   }
9030	   $pause = $false;
9031	}
9032	else{
9033           if ( $playing_music eq $false ){ return;}
9034
9035	   if ($files_info[$selected_row]{extension_input} ne 'cda'){
9036	      if(defined $pid_player){kill 19,$pid_player}; # see <kill -l>
9037	      $pause = $true;
9038	      return $true;
9039           }
9040
9041           if ( $cdplayer eq "cdcd" ){
9042	      my @cmd = ( $cdcd_path , '-d', $audiodevice_path, 'pause' );
9043              exec_cmd_system2(@cmd);
9044           }
9045	   elsif ( $cdplayer eq "gnormalize::cdplay" ){
9046	      gnormalize::cdplay::pause_cdrom();
9047	   }
9048	   elsif ( $cdplayer eq "Audio::CD" ){
9049	      $audiocd->pause;
9050	   }
9051	   $pause = $true;
9052	}
9053	return $true;
9054     }
9055     # fastforward button display
9056     $xdest_but = $xdest_buttons + 3*$buttons_w/2 + 2*$delta;
9057     if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9058        draw_buttons(Press_Forward => $true, Draw_Forward => $true);
9059	$button_press_forward = $true;
9060        #print "vc clicou em ff: x = $x, y = $y ;; height = $height ;; width = $width ;; dcenterx = $dcenterx\n";
9061	if ( $playing_music eq $false ){ return;}
9062
9063	if ($files_info[$selected_row]{extension_input} ne 'cda'){
9064	   $count = $count+15;
9065	   play_selection( skip => $count );
9066	   $pause = $false;
9067	   return $true;
9068        }
9069
9070        if ( $cdplayer eq "cdcd" ){
9071	   my @cmd = ( $cdcd_path , '-d', $audiodevice_path, 'ff', '00:15' ); # advance 15 seconds
9072           exec_cmd_system2(@cmd);
9073        }
9074	elsif ( $cdplayer eq "gnormalize::cdplay" ){
9075	   gnormalize::cdplay::play_cdrom_msf(Track => $selected_row + 1, Advance => 15);    # advance 15 seconds
9076	}
9077	elsif ( $cdplayer eq "Audio::CD" ){
9078	   $audiocd->advance(0, 15);  # advance 15 seconds
9079	}
9080	$count = $count+15;
9081	#show_time();
9082	$pause = $false;
9083	return $true;
9084     }
9085     # next button display
9086     $xdest_but = $xdest_buttons + 5*$buttons_w/2 + 3*$delta;
9087     if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9088        draw_buttons(Press_Next => $true, Draw_Next => $true);
9089	$button_press_next = $true;
9090        #print "vc clicou em next: x = $x, y = $y ;; height = $height ;; width = $width ;; dcenterx = $dcenterx\n";
9091        if ( $playing_music eq $true ){ go_forward(); }
9092	else { $go_forward = $false; }
9093	return $true;
9094     }
9095     # eject button display
9096     $xdest_but = $xdest_buttons + 7*$buttons_w/2 + 4*$delta;
9097     if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9098        draw_buttons(Press_Eject => $true, Draw_Eject => $true);
9099	$button_press_eject = $true;
9100        stop_playing_music();
9101	if ( $cdplayer eq "gnormalize::cdplay" ){
9102	   gnormalize::cdplay::eject_cdrom();
9103	}
9104        elsif ( $cdplayer eq "cdcd" ){
9105	   my @cmd = ( $cdcd_path , '-d', $audiodevice_path, 'eject' );
9106           exec_cmd_system2(@cmd);
9107	   # system("/usr/bin/eject /dev/hdc") || die " Error: $! \n";
9108	   # print " here, eject\n";
9109        }
9110	if ( $cdplayer eq "Audio::CD" ){ $audiocd->eject; }
9111	exec_cmd_system2( 'eject', $audiodevice_path );
9112	entry_cda_change();
9113	if ( not $window->is_active ) { show_only_cd_display(); }
9114	return $true;
9115     }
9116     # rewind button display
9117     $xdest_but = $xdest_buttons - 3*$buttons_w/2 - 1*$delta;
9118     if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9119        draw_buttons(Press_Rewind => $true, Draw_Rewind => $true);
9120	$button_press_rewind = $true;
9121	if ( $playing_music eq $false ){ return;}
9122
9123	if ($count >= 15){ $count = $count-15;}
9124	else { $count = 0;
9125	       $go_back = $true;
9126	}
9127
9128	if ($files_info[$selected_row]{extension_input} ne 'cda'){
9129	   play_selection( skip => $count );
9130	   $pause = $false;
9131	   return $true;
9132        }
9133
9134        if ( $cdplayer eq "cdcd" ){
9135	   my @cmd = ( $cdcd_path , '-d', $audiodevice_path, 'rew', '00:15' ); # go back 15 seconds
9136           exec_cmd_system2(@cmd);
9137        }
9138	elsif ( $cdplayer eq "gnormalize::cdplay" ){
9139	   gnormalize::cdplay::play_cdrom_msf(Track => $selected_row + 1, Advance => -15);  # go back 15 seconds
9140	}
9141	elsif ( $cdplayer eq "Audio::CD" ){
9142	   $audiocd->advance(0, -15);  # go back 15 seconds
9143	}
9144
9145	$pause = $false;
9146	return $true;
9147     }
9148     # back button display
9149     $xdest_but = $xdest_buttons - 5*$buttons_w/2 - 2*$delta;
9150     if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9151        draw_buttons(Press_Back => $true, Draw_Back => $true);
9152	$button_press_back = $true;
9153	if ( $playing_music eq $true and @rows_already_played > 0 ) { go_back(); }
9154        else { $go_back = $false; }
9155	return $true;
9156     }
9157     # volume button display ; $pixbuf_sound
9158     if ( $x >= $xdest_sound and $x <= ($xdest_sound + $sound_size_w) and $y >= $ydest_sound and $y <= ($ydest_sound + $sound_size_h) ){
9159	if ( $cdplayer eq "Audio::CD" or $cdplayer eq "cdcd" or $cdplayer eq "gnormalize::cdplay" ){
9160	   show_buttom_volume2();
9161	   $status_bar->push($context_id, "");
9162	}
9163	return $true;
9164     }
9165     # rand layout ?
9166     if ( $x >= $xdest_random and $x <= ($xdest_random + $random_w) and
9167          $y >= $ydest_random and $y <= ($ydest_random + $random_h) ){
9168	$play_random ^= 1;
9169	draw_random($da,undef);
9170	if($play_random){ draw_text_scrolling( layout => (draw_layout( text => "random on" ))[0], scrolling => $false ); }
9171	else{ draw_text_scrolling( layout => (draw_layout( text => "random off" ))[0], scrolling => $false ); }
9172
9173	if ( defined($timer_wait) ){ Glib::Source->remove($timer_scroll);}  # to not duplicate the MainLoop
9174        my $timer_wait = Glib::Timeout->add (700, \&show_scrolling_text);
9175        return $true;
9176     }
9177     # loop layout
9178     if ( $x >= $xdest_loop and $x <= ($xdest_loop + $loop_w) and
9179          $y >= $ydest_loop and $y <= ($ydest_loop + $loop_h) ){
9180	$loop_tracks ^= 1;
9181	draw_loop($da,undef);
9182	if($loop_tracks){ draw_text_scrolling( layout => (draw_layout( text => "loop on" ))[0], scrolling => $false ); }
9183	else{ draw_text_scrolling( layout => (draw_layout( text => "loop off" ))[0], scrolling => $false ); }
9184
9185	if ( defined($timer_wait) ){ Glib::Source->remove($timer_scroll); }  # to not duplicate the MainLoop
9186        my $timer_wait = Glib::Timeout->add (700, \&show_scrolling_text);
9187        return $true;
9188     }
9189     # time min:sec display
9190     my $dist_dig = 1;
9191     if ( $x >= $xdest_numbers and $x <= (5*($numbers_w + $dist_dig) + $xdest_numbers) and
9192          $y >= $ydest_numbers and $y <= ($ydest_numbers + $numbers_h) ){
9193	$show_time_remaining ^= 1;
9194	if ( not $show_time_remaining){ $show_time_abs ^= 1; }
9195        show_time();
9196        return $true;
9197     }
9198     # draw_text_scrolling  ; change background color
9199     if ( $x >= $xdest and $x <= ($xdest + $scroll_w) and $y >= $ydest and $y <= ($ydest + $scroll_h) ){
9200	cdplayer_color_selection();
9201	sensitive_bg_color(); # cd_player bg color change
9202        return $true;
9203     }
9204     # draw_lapsed_time ; progress bar of elapsed time;
9205     if ( $x >= $xdest_posbar and $x <= ($xdest_posbar + $posbar_length) and
9206          $y >= $ydest_posbar and $y <= ($ydest_posbar + $posbar_h) ){
9207        my $pct = number_value( sprintf ( "%.2f", ($x - $xdest_posbar + 1)/$posbar_length ) ); # 0.000 to 1.000
9208	if ($pct < 0){$pct = 0;}
9209	if ($pct > 1){$pct = 1;}
9210        #print "aqui ; x = $x ; y = $y ; pct = $pct\n";
9211	return if not $playing_music;
9212
9213        if ($files_info[$selected_row]{extension_input} ne 'cda'){
9214	   my $total_sec = time_to_sec( $files_info[$selected_row]{length} );
9215	   $count = sprintf ('%.0f', $pct * $total_sec );
9216	   play_selection( skip => $count );
9217	   return $true;
9218        }
9219
9220	my $track = $selected_row + 1;
9221        my $startframe = $start_sector[$track-1] + 150;  # for absolute frames, sum + 150 (gap)
9222	my $endframe   = $start_sector[$track] + 150;
9223	my $dif        = $endframe - $startframe;
9224        $startframe = int($startframe + $dif*$pct);
9225	$count      = int( $dif*$pct/75 ); # relative seconds
9226	my ($hour,$min,$sec) = sec_to_time( $count );
9227	#print "startframe = $startframe ;  endframe = $endframe ; min:sec = $min:$sec\n";
9228
9229	if ( $cdplayer eq "gnormalize::cdplay" ){ gnormalize::cdplay::play_cdrom_msf(Track => $selected_row + 1, Position => $pct); }
9230	elsif ( $cdplayer eq "Audio::CD"       ){ $audiocd->play_frames($startframe, $endframe);                                  }
9231	elsif ( $cdplayer eq "cdcd" ){
9232	   # play [start track] [end track] [min:sec]
9233	   my @cmd = ( $cdcd_path , '-d', $audiodevice_path, 'play', $track, $track, "$min:$sec" );
9234           exec_cmd_system2(@cmd);
9235        }
9236
9237        return $true;
9238     }
9239
9240  }
9241  # We've handled the event, stop processing
9242  return $true;
9243}
9244
9245###--------------------------------------------------###
9246#####--------------- volume: begin ----------------#####
9247
9248# see the $pixbuf_sound
9249my $window_volume;
9250
9251sub show_buttom_volume2 {
9252   if ( not defined $window_volume ){
9253      show_buttom_volume();
9254   }
9255   else {
9256      $window_volume->hide;
9257      #$window_volume->destroy;
9258      $window_volume = undef;
9259   }
9260}
9261
9262sub show_buttom_volume {
9263   $window_volume = Gtk2::Window->new('popup');
9264   $window_volume->set_decorated ($false);
9265   $window_volume->set_position ('mouse'); # none, center-always, center-on-parent , mouse
9266
9267   my ($position_x, $position_y) = $window_volume->get_position;
9268   #print "position_x = $position_x ;; position_y = $position_y\n";
9269   if ( $position_y > 130 ){ $window_volume->move ($position_x + 72, $position_y - 120 ); }
9270   else { $window_volume->move ($position_x + 72, $position_y + 130 ); }
9271
9272   my $vbox_window_volume = Gtk2::VBox->new;
9273   my $frame_window_volume = Gtk2::Frame->new;
9274   my $vbox2_window_volume = Gtk2::VBox->new;
9275   my $label_window_volume = Gtk2::Label->new;
9276   my $hsep_window_volume = Gtk2::HSeparator->new;
9277   my $label2_window_volume = Gtk2::Label->new(" volume ");
9278
9279   # see <man Gtk2::Scale> and Gtk2::Range ;; Adjust the volume level
9280   # Gtk2::VScale->new_with_range ($min, $max, $step)
9281   $button_volume = Gtk2::VScale->new_with_range (  0, 100, 1);
9282   $button_volume->set_digits(0);
9283   $button_volume->set_draw_value($false);
9284   $button_volume->set_inverted ($true);
9285   #$button_volume->set_value_pos ('left'); # left, right, top, bottom
9286   $button_volume->set_size_request( 40, 130 );
9287   $button_volume->signal_connect ('value-changed', \&change_label_volume, $label_window_volume );
9288   $button_volume->signal_connect ('value-changed', \&change_volume_level );
9289   $button_volume->signal_connect ( 'button_release_event' => \&show_buttom_volume2 );
9290   $button_volume->set_sensitive($true);
9291   # $button_volume->show;
9292
9293   #$button_volume->set_value ( get_volume_level($audio_cd_volume) );
9294   # Get $audio_cd_volume value from config file $setting_file
9295   if ( $audio_cd_volume > 100 or $audio_cd_volume < 0 ){ $audio_cd_volume = 80; }
9296   $button_volume->set_value ( $audio_cd_volume );
9297
9298   $window_volume->add($vbox_window_volume);
9299   $vbox_window_volume->add ($frame_window_volume);
9300   $frame_window_volume->add ($vbox2_window_volume);
9301
9302   $vbox2_window_volume->add ($button_volume);
9303   $vbox2_window_volume->add ($hsep_window_volume);
9304   $vbox2_window_volume->add ($label_window_volume);
9305   $vbox2_window_volume->add ($label2_window_volume);
9306
9307   #my ($width, $height) = $window_volume->get_size;
9308   #print "width = $width ;; height = $height \n";
9309
9310   $window_volume->show_all;
9311}
9312
9313sub change_label_volume {
9314   my $widget = shift;  # $button_volume = Gtk2::VScale
9315   my $label  = shift;  # Gtk2::Label
9316
9317   my $volume = int( $button_volume->get_value );  # 0 - 100
9318   $label->set_label( $volume."%" );               # 0 - 100%;
9319}
9320
9321# Set volume level to play audio cd.
9322sub change_volume_level {
9323   my $widget  = shift;  # $button_volume = Gtk2::VScale
9324   my $get_vol = defined $button_volume ? int($button_volume->get_value) : 80;
9325
9326   my $volume = shift || $get_vol; # Valid volumes: 0 - 100
9327   if ( $volume > 100 or $volume < 0 ){ $volume = 80; }
9328   $audio_cd_volume = $volume;                        # 0 - 100
9329   $volume = sprintf ( "%.0f", $volume * (255/100) ); # 0 - 255
9330
9331   return unless defined $files_info[$selected_row]{extension_input};
9332
9333   if ($files_info[$selected_row]{extension_input} ne 'cda' and $amixer_path ne ""){
9334       # amixer set PCM 90% / amixer set Master 60% ; Mixer_device
9335       my $cmd = "$amixer_path set Master $audio_cd_volume%";
9336       exec_cmd_system($cmd);
9337       $cmd = "$amixer_path set PCM $audio_cd_volume%";
9338       exec_cmd_system($cmd);
9339       #$cmd = "$amixer_path set Front $audio_cd_volume%";
9340       #exec_cmd_system($cmd);
9341       return $audio_cd_volume;
9342   }
9343
9344   # Valid volumes: 0 - 255 ;; volume: front or back
9345   if ( $cdplayer eq "Audio::CD" ){
9346       my $vol = $audiocd->get_volume; # Returns an Audio::CD::Volume object.
9347
9348       # Change the volume channels
9349       $vol->front->left ( $volume );
9350       $vol->front->right( $volume );
9351       $vol->back->left  ( $volume );
9352       $vol->back->right ( $volume );
9353       # Save this changes
9354       $audiocd->set_volume ( $vol );
9355   }
9356   elsif ( $cdplayer eq "gnormalize::cdplay" ){
9357       gnormalize::cdplay::set_vol($volume);
9358   }
9359   elsif ( $cdplayer eq "cdcd" ){
9360       my $cmd = "$cdcd_path -d $audiodevice_path setvol $volume";
9361       exec_cmd_system($cmd);
9362   }
9363   return $audio_cd_volume;
9364}
9365
9366sub get_volume_level { # not used, get the volume level from config file $setting_file
9367   my $volume;
9368   # Valid volumes: 0 - 255 ;; volume: front or back
9369   if ( $cdplayer eq "Audio::CD" ){
9370      my $vol = $audiocd->get_volume; # Returns an Audio::CD::Volume object.
9371      $volume = int ( ($vol->front->left + $vol->front->right)/2  );
9372   }
9373   elsif ( $cdplayer eq "cdcd" ){
9374      my $cmd = "$cdcd_path -d $audiodevice_path getvol";
9375      my $output = exec_cmd_system($cmd);
9376      #         Left  Right
9377      #Front      54     54
9378      #Back        0      0
9379      while( $output =~ /Front\s+(\d+)\s+(\d+).*/g ){
9380         $volume = int ( ($1 + $2)/2  );
9381      }
9382   }
9383   $volume = sprintf ( "%.0f", $volume * (100/255) ); # 0 - 100
9384   return $volume;
9385}
9386
9387#####--------------- volume: final ----------------#####
9388###--------------------------------------------------###
9389
9390# to fill the empty space
9391my $label_fill = new Gtk2::Label( " " );
9392$label_fill->set_alignment( 0.5, 0.5 );
9393$hbox4->pack_start($label_fill, $true, $true, 0);
9394$label_fill->show();
9395
9396# Create Dir_Selection button
9397my $button_play_more = Gtk2::Button->new;
9398$button_play_more->add( DrawIcons('gtk-add','button') );
9399#$button_play_more->set_label("Dir / File");
9400$tooltips->set_tip( $button_play_more, $langs{tip405} ) if $show_all_tooltips;
9401$button_play_more->signal_connect( "clicked", \&dir_selection );
9402$button_play_more->set( 'focus-on-click' => $false, 'relief' => 'none' );
9403$hbox4->pack_start ($button_play_more, $false, $false, 0);
9404$button_play_more->show;
9405
9406
9407#####------------- Mode :: CD display -------------#####
9408###--------------------------------------------------###
9409
9410sub show_only_cd_display {
9411
9412   if ($window->is_active) {    # Hide the main $window
9413
9414      $hbox4->remove($da);
9415      $hbox4->remove($label_fill);
9416      $hbox4->remove($button_play_more);
9417      $window->hide;
9418
9419      $window_cd_player = Gtk2::Window->new('toplevel'); # 'toplevel' or 'popup'
9420      $window_cd_player->set_title( "" ); # no title
9421      $window_cd_player->set_position ('mouse'); # none, center-always, center-on-parent , mouse
9422      $window_cd_player->signal_connect( "delete_event", \&Quit );
9423      $window_cd_player->set( 'allow-shrink' => $false,
9424                              'resizable'    => (not $show_cdplayer_skin),
9425                              'allow-grow'   => (not $show_cdplayer_skin) );
9426      $window_cd_player->resize($window_cd_player_width, $window_cd_player_height);
9427      $window_cd_player->set_title( "gnormalize" ) if ($window_cd_player_width > 100);
9428      $show_cdplayer_keep_above ? $window_cd_player->set_keep_above($true) : $window_cd_player->set_keep_above($false);
9429
9430      $window_cd_player->add($da);
9431      $window_cd_player->set_decorated ($show_cdplayer_border); # boolean = $window->get_decorated
9432      $window_cd_player->realize;
9433      $window_cd_player->show_all;
9434   }
9435   else {                       # Show the main $window
9436      # 'if not defined $pixmap_mask' is the same as 'without use skin'
9437      ($window_cd_player_width, $window_cd_player_height) = $window_cd_player->get_size if not defined $pixmap_mask;
9438      $window_cd_player->remove($da);
9439      $window_cd_player->destroy;
9440      $window_cd_player = undef;
9441
9442      $hbox4->pack_start($da, $false, $false, 0);
9443      $hbox4->pack_start($label_fill, $true, $true, 0);
9444      $hbox4->pack_start ($button_play_more, $false, $false, 0);
9445      $window->set_position ('mouse');
9446      $window->show;
9447   }
9448}
9449
9450sub motion_notify_event_cdplayer {   # See /usr/share/doc/perl-Gtk2-1.080/examples/scribble.pl
9451  my ($widget, $event, @data) = @_;  # $widget = $da = Gtk2::DrawingArea ; GdkEventMotion *event
9452                                     # Gtk2::Gdk::Event::Motion
9453
9454  my $height = $widget->allocation->height;
9455  my $width  = $widget->allocation->width;
9456
9457  #return if $window_cd_player is decorated;  $window->get_decorated  ;
9458  my ($x, $y, $state);
9459
9460  if ($event->is_hint) {
9461     (undef, $x, $y, $state) = $event->window->get_pointer;
9462  }
9463  else {
9464     $x = $event->x;
9465     $y = $event->y;
9466     $state = $event->state;
9467  }
9468  my ( $x_root , $y_root ) = ( $event->x_root , $event->y_root );
9469  my ($xoffset, $yoffset) = $event -> window() -> get_root_origin();
9470  #print "x = $x ; y = $y ; x_root = $x_root ; y_root = $y_root ; xoffset = $xoffset ; yoffset = $yoffset\n";
9471
9472  if ( $state >= "button1-mask" and defined $window_cd_player ) {
9473     if ( not $window_cd_player->get_decorated ) {
9474        # get (click_x, click_y) with button_press_event
9475        $window_cd_player->move( $x_root - $click_x , $y_root - $click_y); # move the $window_cd_player
9476     }
9477  }
9478
9479  if ( $y < $height/3  ){ return $true; }
9480
9481  my $xdest_but = $xdest_buttons - 1*$buttons_w/2 + 0*$delta;
9482  # stop button display
9483  if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9484     draw_buttons(Shadow_Stop => $true, Draw_Stop => $true);
9485  }
9486  else { draw_buttons(Draw_Stop => $true); }  # default color
9487  # (play || pause) buttons display
9488  $xdest_but = $xdest_buttons + 1*$buttons_w/2 + 1*$delta;
9489  if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9490     draw_buttons(Shadow_Play => $true, Draw_Play => $true);
9491  }
9492  else { draw_buttons(Draw_Play => $true); }
9493  # fastforward button display
9494  $xdest_but = $xdest_buttons + 3*$buttons_w/2 + 2*$delta;
9495  if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9496     draw_buttons(Shadow_Forward => $true, Draw_Forward => $true);
9497  }
9498  else { draw_buttons(Draw_Forward => $true); }
9499  # next button display
9500  $xdest_but = $xdest_buttons + 5*$buttons_w/2 + 3*$delta;
9501  if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9502     draw_buttons(Shadow_Next => $true, Draw_Next => $true);
9503  }
9504  else { draw_buttons(Draw_Next => $true); }
9505  # eject button display
9506  $xdest_but = $xdest_buttons + 7*$buttons_w/2 + 4*$delta;
9507  if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9508     draw_buttons(Shadow_Eject => $true, Draw_Eject => $true);
9509  }
9510  else { draw_buttons(Draw_Eject => $true); }
9511  # rewind button display
9512  $xdest_but = $xdest_buttons - 3*$buttons_w/2 - 1*$delta;
9513  if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9514     draw_buttons(Shadow_Rewind => $true, Draw_Rewind => $true);
9515  }
9516  else { draw_buttons(Draw_Rewind => $true); }
9517  # back button display
9518  $xdest_but = $xdest_buttons - 5*$buttons_w/2 - 2*$delta;
9519  if ( $x >= $xdest_but and $x <= ($xdest_but + $buttons_w) and $y >= $ydest_buttons and $y <= ($ydest_buttons + $buttons_h) ){
9520     draw_buttons(Shadow_Back => $true, Draw_Back => $true);
9521  }
9522  else { draw_buttons(Draw_Back => $true); }
9523
9524  #print "x = $x ; y = $y ; x_root = $x_root ; y_root = $y_root ; xoffset = $xoffset\n";
9525  return $true;
9526}
9527
9528
9529#------------------------- final -----------------------#
9530###---------------------- Display --------------------###
9531
9532###---------------------- Frame6 ---------------------###
9533#--------------------------About------------------------#
9534# Construct a textview to put About
9535my $textview_about = Gtk2::TextView->new;
9536$textview_about->set (    'editable' => $false,
9537                    'cursor_visible' => $false,
9538		       'left_margin' => 10, 'right_margin' => 10,
9539                         'wrap-mode' => 'word');
9540my $color_about = Gtk2::Gdk::Color->parse ("#E6EDBD");
9541$textview_about->modify_base ('normal', $color_about);
9542$textview_about->show;
9543
9544$frame6->add($textview_about);
9545# my $container = $frame6->get_children;  # see <man Gtk2::Container>
9546$textview_about->set_border_width (4);
9547my $color_textview_about = Gtk2::Gdk::Color->parse ("#A0B8A3");
9548$textview_about->modify_bg ('normal', $color_textview_about);
9549
9550# see <man Gtk2::TextBuffer>
9551# $buffer = Gtk2::TextBuffer->new;
9552my $buffer_about = $textview_about->get_buffer;
9553$buffer_about->create_tag ("italic_about",   'foreground' => "red",   # see <man Gtk2::TextTag> for more options
9554                                            #'background' => "yellow",
9555                                            justification => 'center',
9556                                                     font => "Helvetica Italic",
9557				            'size-points' => "15");
9558$buffer_about->create_tag ("version",        'foreground' => "blue",
9559                                            justification => 'center',
9560					             font => "Helvetica",
9561					    'size-points' => "12");
9562$buffer_about->create_tag ("descr",          'foreground' => "black",
9563                                            justification => 'center',
9564					             font => "Sans",
9565					    'size-points' => "10");
9566
9567my $count_textview = 1; my $count_same_gif = 1; my $next_string = 1;
9568my $timer_about;
9569
9570sub tux_animation_textview {
9571   $textview_about->get_buffer->delete($textview_about->get_buffer->get_bounds); #clear
9572   my $iter_about = $buffer_about->get_iter_at_offset (0); # get start of buffer
9573   $buffer_about->insert_with_tags_by_name ($iter_about, "gnormalize", "italic_about");
9574   $buffer_about->insert_with_tags_by_name ($iter_about, " - ", "descr");
9575   $buffer_about->insert_with_tags_by_name ($iter_about, "version $VERSION\n", "version");
9576
9577   # ---------------------- animation --start-------------------#
9578   # See /usr/share/doc/perl-Gtk2-1.080/gtk-demo/textview.pl
9579   my $animation = choose_random_animation(); # animation path filename
9580   my $widget_animation;
9581   eval { $widget_animation = Gtk2::Image->new_from_file ($animation); };
9582   if ($widget_animation and -e filename_from_unicode $animation and $check_button_animation->get_active) {
9583
9584      $buffer_about->insert_with_tags_by_name ($iter_about, " ", "descr"); # to center line
9585      my $anchor = $buffer_about->create_child_anchor ($iter_about);
9586      $textview_about->add_child_at_anchor ($widget_animation, $anchor);
9587      $widget_animation->show;
9588
9589      # textmark = $buffer->create_mark ($mark_name, $where, $left_gravity)
9590      $buffer_about->create_mark ('mark', $iter_about, $true);
9591
9592      # see <man Glib::MainLoop>
9593      # integer = Glib::Timeout->add ($interval, $callback, $data=undef, $priority=G_PRIORITY_DEFAULT)
9594      if ( defined($timer_about) ){ Glib::Source->remove($timer_about);}  # to not duplicate the MainLoop
9595      if ( $animation =~ /typing/i ){ $timer_about = Glib::Timeout->add (250, \&show_typing_char); }
9596      else { $timer_about = Glib::Timeout->add (20000, \&choose_textview_about); } # 15000 milliseconds = 15 seconds
9597   }
9598   # ---------------------- animation --final-------------------#
9599   else {
9600      if ( defined($timer_about) ){ Glib::Source->remove($timer_about);}  # remove the MainLoop
9601      $count_textview = 1; $count_same_gif = 1;
9602      $buffer_about->insert_with_tags_by_name ($iter_about, "\n$langs{msg040}", "descr");
9603      $buffer_about->insert_with_tags_by_name ($iter_about, "\n\n$langs{msg064}", "descr");
9604      $buffer_about->insert_with_tags_by_name ($iter_about, "\n\n$langs{msg093}", "descr");
9605      $buffer_about->insert_with_tags_by_name ($iter_about, "\n\n$langs{msg094}", "descr");
9606   }
9607}
9608
9609sub choose_textview_about {
9610   my $textmark = $buffer_about->get_mark ('mark');
9611   my $iter_mark = $buffer_about->get_iter_at_mark ($textmark);
9612   #$iter_mark = $buffer_about->get_iter_at_line ( 3 );
9613
9614   my $iter_end = $buffer_about->get_end_iter;
9615   $textview_about->get_buffer->delete($iter_mark , $iter_end ); #clear
9616
9617   if ($count_textview == 1){ $buffer_about->insert_with_tags_by_name ($iter_mark, "\n$langs{msg084}", "descr"); }
9618   if ($count_textview == 2){ $buffer_about->insert_with_tags_by_name ($iter_mark, "\n$langs{msg085}", "descr"); }
9619   if ($count_textview == 3){ $buffer_about->insert_with_tags_by_name ($iter_mark, "\n$langs{tip429}", "descr"); }
9620   if ($count_textview == 4){ $buffer_about->insert_with_tags_by_name ($iter_mark, "\n$langs{msg064}", "descr"); }
9621   if ($count_textview == 5){ $buffer_about->insert_with_tags_by_name ($iter_mark, "\n$langs{msg093}", "descr"); }
9622   if ($count_textview == 6){ $buffer_about->insert_with_tags_by_name ($iter_mark, "\n$langs{msg094}", "descr"); $count_textview = 0; $count_same_gif += 1;}
9623   if ($count_same_gif > 2 + int(rand(8)) and $count_textview == 1 ){
9624      $count_textview = 1; # start value
9625      $count_same_gif = 1;
9626      tux_animation_textview();
9627      return $false;
9628   }
9629   $count_textview += 1;
9630   return $true; # # if return is $false; then stop $timer_about loop
9631}
9632
9633sub show_typing_char {
9634   my $string;
9635   if ($next_string == 1){$string = "\n$langs{msg084}";}
9636   if ($next_string == 2){$string = "\n$langs{msg085}";}
9637   if ($next_string == 3){$string = "\n$langs{tip429}";}
9638   if ($next_string == 4){$string = "\n$langs{msg064}";}
9639   if ($next_string == 5){$string = "\n$langs{msg093}";}
9640   if ($next_string == 6){$string = "\n$langs{msg094}";}
9641   if ($next_string >= 7){
9642      $count_textview = 1; # start value
9643      $next_string = 1;
9644      tux_animation_textview();
9645      return $false;
9646   }
9647   my $iter_mark = $buffer_about->get_iter_at_mark ( $buffer_about->get_mark ('mark') );
9648   my $iter_end = $buffer_about->get_end_iter;
9649   $textview_about->get_buffer->delete($iter_mark , $iter_end ); #clear
9650   #                substr EXPR,OFFSET,LENGTH
9651   my $new_string = substr($string,  0, $count_textview);
9652   $buffer_about->insert_with_tags_by_name ($iter_mark, $new_string , "descr");
9653
9654   if ( length($new_string) >= length($string) ) { $count_textview = 0; $next_string += 1; }
9655   $count_textview += 1;
9656   return $true; # # if return is $false; then stop $timer_about loop
9657}
9658
9659sub choose_random_animation {
9660   my @array_animation = (  '/usr/local/share/gnormalize/animations/super_tux.gif',
9661                            '/usr/local/share/gnormalize/animations/dancing_penguin.gif',
9662			    '/usr/local/share/gnormalize/animations/tux_lunapaint.gif',
9663			    '/usr/local/share/gnormalize/animations/penguin_computer.gif',
9664			    '/usr/local/share/gnormalize/animations/penguin_and_camel.gif',
9665			    '/usr/local/share/gnormalize/animations/penguin_typing.gif',
9666			    '/usr/local/share/gnormalize/animations/penguin_cold.gif',
9667			    '/usr/local/share/gnormalize/animations/penguin_ice.gif'
9668			 );
9669   my $count_gif = $#array_animation + 1; # Get the number of elements.
9670   my $rand = int( rand( $count_gif ) );  # pick one random element: 0  <=  rand($count_gif)  <  $count_gif
9671   #print " rand[1---N] = $rand ; animation = $array_animation[$rand] ; count_gif = $count_gif\n";
9672   return $array_animation[$rand];
9673}
9674
9675tux_animation_textview();
9676
9677######--------------------------------------------######
9678
9679#####------------- h b o x _normalize--------------#####
9680###--------------------- start ----------------------###
9681
9682# New Button : Show Info
9683my $button_show_info = Gtk2::Button->new;
9684$button_show_info->add( DrawIcons('gtk-goto-bottom','button') );
9685#$button_show_info->set( 'focus-on-click' => $false, 'relief' => 'none' );
9686$button_show_info->signal_connect( clicked => \&button_with_4_phases, '1');
9687#$button_show_info->set( 'focus-on-click' => $false, 'relief' => 'none' );
9688$button_show_info->show;
9689
9690# New Button : Hide Info
9691my $button_hide_info = Gtk2::Button->new;
9692$button_hide_info->add( DrawIcons('gtk-goto-top','button') );
9693$button_hide_info->signal_connect( clicked => \&button_with_4_phases, '1');
9694$button_hide_info->show;
9695
9696# this align is a container that hold two buttons alternately
9697my $align_hold_2_buttons = Gtk2::Alignment->new(0.5, 0.5 , 0, 0);
9698$align_hold_2_buttons->add ($button_show_info);
9699$hbox_normalize->pack_start( $align_hold_2_buttons, $false, $false, 0 );
9700$align_hold_2_buttons->show;
9701
9702#---------------------------------------#
9703
9704my $normalize_button = Gtk2::ToggleButton->new("normalize");
9705$normalize_button->signal_connect( "toggled", \&toggle_button_normalize );
9706#my $color_green = Gtk2::Gdk::Color->parse ("#B1C3B2");
9707#$normalize_button->modify_bg ('normal', $color_green);
9708# a tabela ir� expandir de acordo com o tamando do hbox
9709$hbox_normalize->pack_start( $normalize_button, $true, $true, 0 );
9710$normalize_button->show();
9711
9712# to be implemented: Thread. See <man Thread::Queue>,
9713# <man threads::shared> and tutorial <man perlthrtut>
9714
9715my $rip_sucess = $false; # get $true if rip is completed
9716
9717##--------------------------------------------------------------##
9718#      this is the brain that control all normalize processes    #
9719##--------------------------------------------------------------##
9720
9721sub toggle_button_normalize {
9722    # To avoid exec the sub two times when the normalize button is pressed
9723    if ( not $normalize_button->get_active ){ return $false; }
9724
9725    if ( $extension_input eq 'other' ){
9726        $normalize_button->set_active($false);
9727	$status_bar->push($context_id, $langs{msg003} );
9728	return $false;
9729    }
9730    elsif ( $extension_input eq 'error' ){
9731        $normalize_button->set_active($false);
9732	$status_bar->push($context_id, $langs{msg004} );
9733	return $false;
9734    }
9735
9736    # Test if the $directory is writable. -w : file is writable by effective uid/gid.
9737    if ( ! -w filename_from_unicode $directory_output ){ $status_bar->push($context_id, " $directory_output ".$langs{msg012} ); return $false;}
9738
9739    # Not change the output extension frames while any encoding process
9740    $hbox1->set_sensitive($false);
9741
9742    my @array; # fill with only the files that is toggled on treeview_play.
9743    if ($normalize_button->get_active){
9744
9745       my %treeview_play_info = get_treeview_play_informations();
9746       @array = @{$treeview_play_info{array_checked}};  # get reference to array_checked that has files toggled.
9747
9748       if ( @array >= 1 ) { search_track_and_rip_dec(@array);  }
9749       else { $status_bar->push($context_id, $langs{msg013} ); }
9750    }
9751
9752    # Show the final messages
9753    if ($extension_input eq 'cda' and @array >= 1){
9754       if ($rip_sucess eq $true and $normalize_button->get_active){ $status_bar->push($context_id, $langs{msg014} ); }
9755       else { $status_bar->push($context_id, $langs{msg015} );}
9756    }
9757    if ($extension_input ne 'cda' and @array >= 1){
9758       if ($norm_type eq "None"){  $status_bar->push($context_id, $langs{msg016} );}
9759       elsif ($extension_input ne $extension_output){ $status_bar->push($context_id, $langs{msg017} );}
9760       else { $status_bar->push($context_id, $langs{msg018} );}
9761    }
9762    # Return the output extension frames to original state after processes
9763    $hbox1->set_sensitive($true);
9764
9765    # After stop the process, label is changed.
9766    if ($extension_input eq 'cda'){ $normalize_button->set_label("rip + normalize + encode"); }
9767    else { $normalize_button->set_label("normalize"); }
9768    change_font_for_all_child($window_font_name,$normalize_button);
9769
9770    # before exit, set the normalize_button to false:
9771    $normalize_button->set_active($false);
9772
9773} # final of ''toggle_button_normalize''
9774
9775sub search_track_and_rip_dec {
9776   my @files_that_is_toggled = @_;
9777
9778   for (my $file = 0; $file <= $#files_that_is_toggled ; $file++){
9779
9780      my $row = $files_that_is_toggled[$file];
9781      next unless $files_info[$row]{rip};
9782      if ( $files_info[$row]{extension_input} eq 'cda' ) { $da->set_sensitive($false); stop_playing_music( change_color => $false ); }
9783
9784      # for the case when the file is deleted or changed after pressed the normalize_button
9785      if ( not -e filename_from_unicode $files_info[$row]{filepath} and $files_info[$row]{extension_input} ne 'cda' ){
9786         print "\nsearch_track_and_rip_dec --> Can't find $files_info[$row]{filepath} file to normalize: $!\n";
9787         $status_bar->push($context_id, " Can't find $files_info[$row]{filepath} file to normalize!");
9788         next;
9789      }
9790      if ( $files_info[$row]{rip} and $normalize_button->get_active ){  # if the music/track is toggled, then normalize it
9791
9792         $normalize_button->set_label( $files_info[$row]{extension_input} eq 'cda' ? $langs{msg019} : "stop decoding" );  # stop ripping/decoding
9793	 change_font_for_all_child($window_font_name,$normalize_button);
9794
9795	 my %hash = determine_directory_and_filename_and_extension(row => $row);
9796	 my $file_wav = $hash{file_wav};
9797	 #print "\nsearch_track_and_rip_dec ---> file_wav = \"$file_wav\" ;; \$row = $row\n";
9798	 # used by make_directory_final();
9799
9800	 $files_info[$row]{technical_info} ? set_tag_info($row) : get_info_from_file(%hash);   # get all mp3/mp4/mpc/... info if necessary
9801         set_output_and_refresh_progress_bar(%hash);
9802         refresh_input_output_arrows();
9803
9804	 insert_msg( "$langs{msg078}: " , "purple");	# "Selected file"
9805         insert_msg( "$hash{filename}\n" , "bg_yellow");
9806
9807	 color_the_selected_tracks_and_scroll( select_color => 'playing', scroll => $true, row => $row );
9808
9809	 # ripping/decoding start here  ;;  $rip_sucess get $true if the rip is completed
9810	 insert_msg(" --- ripping ---\n" , "small-green") if ($files_info[$row]{extension_input} eq 'cda');
9811	 insert_msg(" --- decoding ---\n", "small-green") if ($files_info[$row]{extension_input} ne 'cda' and $files_info[$row]{extension_input} ne 'wav');
9812
9813	 my $track = sprintf("%02d", $row + 1 ); # 02d : leading zero
9814	 if    ($files_info[$row]{extension_input} eq 'cda' and $ripper eq "cdparanoia"){ $rip_sucess = rip_audio_cdparanoia($track,$file_wav);}
9815	 elsif ($files_info[$row]{extension_input} eq 'cda' and $ripper eq "cdda2wav"  ){ $rip_sucess = rip_audio_cdda2wav($track,$file_wav);  }
9816
9817	 if ( $normalize_button->get_active ){
9818	    # decode, normalize and encode the files
9819            dec_norm_enc(%hash);   # %hash have many information about the file.
9820	 }
9821	 color_the_selected_tracks_and_scroll( select_color => 'played', scroll => $false, row => $row);
9822
9823	 last unless $normalize_button->get_active;
9824      }
9825   } # final of ''for (my $file = 0; $file <= $#files_that_is_toggled ; $file++){''
9826
9827   if ( defined $cdcd_path or $use_audiocd eq $true or $os =~ /Linux/i ){ $da->set_sensitive($true); }
9828}
9829
9830## decode, normalize and encode - for mp3/ogg/wav files
9831sub dec_norm_enc {
9832   my %args = ( @_,  # argument pair list goes here
9833	      );
9834
9835   my $file_mp3 = $args{file_mp3}; my $file_mp4 = $args{file_mp4};  my $file_mpc   = $args{file_mpc};
9836   my $file_ogg = $args{file_ogg}; my $file_ape = $args{file_ape};  my $file_flac  = $args{file_flac};
9837   my $file_cda = $args{file_cda}; my $file_wav = $args{file_wav};
9838   my $filename = $args{filename}; my $dir      = $args{directory}; my $extension_input = $args{extension_input};
9839   my $filepath = $args{filepath};
9840
9841   #print "\ndec_norm_enc --> (\$directory,\$file_wav) = (\"$dir\",\"$file_wav\") ;; \$extension_input = $extension_input ;; row = $args{row}\n\n";
9842
9843   my $insensibility = $spinner_sensi->get_value;
9844   $priority = $spinner_pri->get_value; #priority
9845
9846   # decode, normalize and encode the files
9847   # decode:
9848   if    ( $extension_input eq 'mp3'  and $lame_path ne ""   ){ decode_mp3_to_wav(%args); }
9849   elsif ( $extension_input eq 'mp4'  and $faad_path ne ""   ){ decode_mp4_to_wav(%args); }
9850   elsif ( $extension_input eq 'mpc'  and $mpcdec_path ne "" ){ decode_mpc_to_wav(%args); }
9851   elsif ( $extension_input eq 'ogg'  and $oggdec_path ne "" ){ decode_ogg_to_wav(%args); }
9852   elsif ( $extension_input eq 'ape'  and $ape_path ne ""    ){ decode_ape_to_wav(%args); }
9853   elsif ( $extension_input eq 'flac' and $flac_path ne ""   ){ decode_flac_to_wav(%args);}
9854   elsif ( $extension_input eq 'wav'  and $directory_output ne $dir ){
9855      my @cmd = ( 'cp', '-f', $dir.'/'.$filename, $directory_output );
9856      insert_msg("." , "small-ini");
9857      insert_msg("@cmd" , "small"); #print on debug textview
9858      insert_msg("\n" , "small");
9859      exec_cmd_system2(@cmd);
9860   }
9861
9862   # normalize:
9863   if ( $normalize_button->get_active ) {
9864      $normalize_button->set_label("stop normalizing");
9865      change_font_for_all_child($window_font_name,$normalize_button);
9866      insert_msg(" --- normalizing ---\n" , "small-green");
9867      $pbar->set_fraction(1) if ($extension_input ne 'wav');
9868      normalize_wav(%args);
9869   }
9870   # [[$treeview_play->get_selection]] is an Gtk2::TreeSelection
9871   # $treeview_play->get_selection->unselect_all;
9872
9873   # encode:
9874   # if $check_change_properties not active and file is already normalized and has the same extension_input then make copy, otherwise the file will be encoded.
9875   if ( $normalize_button->get_active ) {
9876
9877      if ( $extension_input eq $extension_output and ( $already_normalized or abs($adjust) <= $insensibility ) and not $check_change_properties->get_active ){
9878           # Then make a copy, don't need to encode. Note that 'cda' never satisfy this conditons
9879	   same_extension_overwrite(%args);
9880      }
9881      else { # $file_cda always satisfy this conditon
9882           $normalize_button->set_label("stop encoding");
9883	   change_font_for_all_child($window_font_name,$normalize_button);
9884	   insert_msg(" --- encoding ---\n" , "small-green");
9885
9886           if    ( $button_output_mp3->get_active ) { $args{file_output} = $args{file_mp3};  encode_wav_to_mp3(%args); }
9887          #if    ( $button_output_mp3->get_active ) { $args{file_output} = $args{file_mp3};  encode_wav_to_wma(%args); }
9888
9889	   elsif ( $button_output_mp4->get_active ) { $args{file_output} = $args{file_mp4};  encode_wav_to_mp4(%args); }
9890	   elsif ( $button_output_mpc->get_active ) { $args{file_output} = $args{file_mpc};  encode_wav_to_mpc(%args); }
9891	   elsif ( $button_output_ogg->get_active ) { $args{file_output} = $args{file_ogg};  encode_wav_to_ogg(%args); }
9892	   elsif ( $button_output_ape->get_active ) { $args{file_output} = $args{file_ape};  encode_wav_to_ape(%args); }
9893	   elsif ( $button_output_flac->get_active) { $args{file_output} = $args{file_flac}; encode_wav_to_flac(%args);}
9894	   elsif ( $button_output_wav->get_active ) { $args{file_output} = $args{file_wav};  encode_wav_to_wav(%args); }  # actually, don't need to encode
9895
9896	   finalize_process(%args);
9897      }
9898   }
9899}
9900
9901
9902## stop a process by the pid number
9903# getppid Returns the process id of the parent process.
9904sub StopProcessing {  # I see dvd2dvix3pass code with some changes
9905   my ($pid,$mesg,$file_wav) = @_;
9906   ## get lame (or normalize, ...) pid
9907   ## try to kill pid+1, else pid
9908   #print ("\n $mesg which pid = $pid\n");
9909   if( $pid >= 0 )  # if some $pid >=0 then some process is running
9910   {
9911	   ## kill the lame/normalize/... PID
9912	   ++$pid;
9913	   my $processeskilled = kill 9, $pid;
9914	   if( $processeskilled == 0 )
9915	   {
9916	      --$pid;
9917	      print "STOP:: pid ".($pid+1)." failed, trying kill pid $pid \n";
9918	      $processeskilled = kill 9, $pid;
9919	      if( $processeskilled == 0 )
9920	      {   print "Could not $mesg : pid = $pid\n";}
9921           }
9922   }
9923   #remove wav file if button_del_wav is active and $file_wav exist
9924   if ($check_button_del_wav->get_active and -e filename_from_unicode "$directory_output/$file_wav" ){
9925      my @cmd = ( 'rm', '-f', $directory_output.'/'.$file_wav );
9926      exec_cmd_system2(@cmd);
9927
9928   }
9929   # In the case of stop normalize process, a file like _normAAAAAA is created,
9930   # them it will be removed, if it exist.	my $file_norm =~ /^\_norm(\w){6}$/i;
9931   if ( -e filename_from_unicode "$directory_output/_normAAAAAA" ){
9932      my @cmd = ( 'rm', '-f', $directory_output.'/_normAAAAAA' );
9933      exec_cmd_system2(@cmd);
9934   }
9935   $status_bar->push($context_id, $mesg);
9936   insert_msg("$mesg", "small-red");
9937   insert_msg("\n" , "small");
9938   return -1; # set (pid = -1) assignnig to a stopped process
9939}
9940
9941sub kill_prog { # using the command "ps" ; not used
9942   my $prog = shift;
9943   my $ps_path = find_prog( Progs => "ps", Comment => "NO_COMMENT");
9944   return if $ps_path eq "";
9945   my $output = exec_cmd_system($ps_path);
9946   while( $output =~ /(\d+)\s+pts.*$prog/g ){  # 1874 pts/3    00:00:00 bash
9947      kill 9,$1;  # print "kill pid number $1 of $prog\n";
9948   }
9949}
9950
9951###--------------------------------------------------###
9952
9953# Create "Quit" button using the icon and text from
9954# the specified stock item, see Gtk2::Stock
9955# print ( "Stoks: ",Gtk2::Stock->list_ids ,"\n");
9956$button_quit = Gtk2::Button->new;
9957$button_quit->add( DrawIcons('gtk-quit','button') );
9958$tooltips->set_tip( $button_quit, $langs{tip121} ) if $show_all_tooltips;
9959$button_quit->signal_connect( "clicked", \&Quit );
9960#$button_quit->set_size_request( 70, -1 );
9961$hbox_normalize->pack_start( $button_quit, $false, $true, 0 );
9962#$button_quit->modify_bg ('normal', $color_blue);
9963#$button_quit->set( 'focus-on-click' => $false, 'relief' => 'none' );
9964$button_quit->show();
9965
9966#####----------------T a b l e _ down--------------#####
9967###--------------------------------------------------###
9968
9969my $da_arrow_dec = Gtk2::DrawingArea->new;
9970# set a minimum size
9971$da_arrow_dec->set_size_request (78, 20);
9972$table_down->attach ($da_arrow_dec, 0, 1, 0, 1, 'fill', 'shrink', 0, 0);
9973$da_arrow_dec->signal_connect (expose_event => \&draw_arrow_dec);
9974$da_arrow_dec->show;
9975
9976$change_fonte = $true;
9977my $size_input;
9978
9979sub draw_arrow_dec { # decoder
9980   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
9981
9982   # to expose this event
9983   if ( $extension_input eq 'other' ){ draw_input_output($da_arrow_dec,undef,"mp3","wav"); } # default
9984   else { draw_input_output($da_arrow_dec,undef,("$extension_input","wav")); }
9985
9986   return $true;
9987}
9988
9989my $width_lay;  my $height_lay; # to determine the font size
9990
9991sub draw_input_output {
9992   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
9993
9994   my $height = $widget->allocation->height;  # height of $widget $da
9995   my $width = $widget->allocation->width ;
9996
9997   my $gc = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
9998   my $color = Gtk2::Gdk::Color->new (184*256, 211*256, 193*256); # #B8D3C1 = 184 211 193 (RGB)
9999   $gc->set_rgb_fg_color ($color);
10000   my $gc1 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
10001   my $color1 = Gtk2::Gdk::Color->new (100*256, 100*256, 200*256); #RGB = 85 90 190
10002   $gc1->set_rgb_fg_color ($color1);
10003   my $purple = Gtk2::Gdk::Color->parse ('purple');
10004   my $blue = Gtk2::Gdk::Color->parse ('blue');
10005   my $color3 = Gtk2::Gdk::Color->parse ('#E6EDBD');
10006
10007   $widget->window->draw_rectangle ($gc, $true, 0, 0, $width, $height);
10008
10009   my $border = 5;
10010   my $width_a = $height - 2*$border;
10011   # arrow
10012   $widget->window->draw_rectangle ($gc1, $true, $width/2 - 7, $height/2 - 2, 8, 4);
10013   $widget->window->draw_polygon ($gc1, $true,
10014                                  $width/2, $height - $border,
10015				  $width/2, $border,
10016                                  $width/2 + $width_a, $height/2 );
10017
10018   my $layout = $widget->create_pango_layout ("");
10019   my $context = $layout->get_context;
10020   my $fontdesc = $context->get_font_description;
10021   $fontdesc->set_family ("Sans");
10022
10023   if ( $change_fonte eq $true ){ # find the nice size and change the font only one time
10024      my $min_width = 20;
10025      $width_lay = $min_width;
10026      $size_input = 2;
10027      while ( $width_lay <= $min_width ){  # to determine the minimum size
10028         $fontdesc->set_size ( $size_input * PANGO_SCALE );
10029         $layout->set_text ("wav"); # only to get its width and height
10030         ($width_lay, $height_lay) = $layout->get_pixel_size;
10031	 $size_input += 1 if ($width_lay <= $min_width) ;
10032      }
10033      $change_fonte = $false;
10034   }
10035   else{ $fontdesc->set_size ( $size_input * PANGO_SCALE ); }
10036
10037   #print " input = $data[0] ;; output = $data[1] ;; size = ",$size_input-1,"\n";
10038
10039   $layout->set_text ($data[0]); # $data = "mp3"
10040   ($width_lay, $height_lay) = $layout->get_pixel_size;
10041   $widget->window->draw_layout_with_colors ($gc1, 2, $height/2 - $height_lay/2, $layout, $blue, $color);
10042
10043   $layout->set_text ($data[1]); # $data = "wav"
10044   ($width_lay, $height_lay) = $layout->get_pixel_size;
10045   $widget->window->draw_layout_with_colors ($gc1, $width - $width_lay - 2, $height/2 - $height_lay/2, $layout, $blue, $color);
10046
10047   return $true;
10048}
10049
10050########-------------Progress-Bar----------------#######
10051
10052# Create the Gtk2::ProgressBar to decode
10053$pbar = Gtk2::ProgressBar->new;
10054#$table_down->attach_defaults ($pbar, 1, 4, 0, 1);
10055$table_down->attach($pbar,  1, 4, 0, 1, ['expand','fill'], 'shrink', 0, 0);
10056$pbar->set_size_request( 420, 24 ); # width, height
10057#$pbar->set_text($file_mp3);
10058$pbar->set_orientation('left_to_right');
10059$pbar->show;
10060
10061
10062my $da_norm = Gtk2::DrawingArea->new;
10063# set a minimum size
10064$da_norm->set_size_request (76, 20);
10065$table_down->attach ($da_norm, 0, 1, 1, 2, 'fill', 'shrink', 0, 0);
10066$da_norm->signal_connect (expose_event => \&draw_norm);
10067$da_norm->show;
10068
10069$change_fonte_norm = $true;
10070my $size_norm;
10071
10072sub draw_norm {
10073   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
10074
10075   my $height = $widget->allocation->height;  # height of $widget $da
10076   my $width = $widget->allocation->width ;
10077
10078   my $gc = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
10079   my $color = Gtk2::Gdk::Color->new (184*256, 211*256, 193*256); # #B8D3C1 = 184 211 193 (RGB)
10080   $gc->set_rgb_fg_color ($color);
10081   my $gc1 = Gtk2::Gdk::GC->new ($widget->window); #  ($widget->window) = Gtk2::Gdk::Drawable
10082   my $color1 = Gtk2::Gdk::Color->new (100*256, 100*256, 200*256); #RGB = 100 100 200
10083   $gc1->set_rgb_fg_color ($color1);
10084   my $purple = Gtk2::Gdk::Color->parse ('purple');
10085   my $blue = Gtk2::Gdk::Color->parse ('blue');
10086   my $color3 = Gtk2::Gdk::Color->parse ('#E6EDBD');
10087
10088   $widget->window->draw_rectangle ($gc, $true, 0, 0, $width, $height);
10089
10090   my $border = 5;
10091   my $width_a = 10;
10092   # arrow
10093   $widget->window->draw_rectangle ($gc1, $true, $width/2 - 6, $height/2 - 2, 8, 4);
10094   $widget->window->draw_polygon ($gc1, $true,
10095                                  $width/2 - $width_a/2 +5, $height - $border,
10096				  $width/2 - $width_a/2 +5, $border,
10097                                  $width/2 + $width_a/2 +5, $height/2 );
10098
10099   my $layout = $widget->create_pango_layout ("");
10100   my $context = $layout->get_context;
10101   my $fontdesc = $context->get_font_description;
10102   $fontdesc->set_family ("Sans");
10103
10104   if ( $change_fonte_norm eq $true ){ # find the nice size and change the font only one time
10105      my $min_width = 60;
10106      $width_lay = $min_width;
10107      $size_norm = 2;
10108      while ( $width_lay <= $min_width ){  # to determine the minimum size
10109         $fontdesc->set_size ( $size_norm * PANGO_SCALE );
10110         $layout->set_text ("normalize"); # only to get its width and height
10111         ($width_lay, $height_lay) = $layout->get_pixel_size;
10112         $size_norm = $size_norm + 1;
10113      }
10114      $change_fonte_norm = $false;
10115   }
10116   else{ $fontdesc->set_size ( ($size_norm - 1) * PANGO_SCALE ); }
10117
10118   $layout->set_text ("normalize");
10119   ($width_lay, $height_lay) = $layout->get_pixel_size;
10120   $widget->window->draw_layout_with_colors ($gc1, $width/2 - $width_lay/2, $height/2 - $height_lay/2, $layout, $blue, $color);
10121
10122   return $true;
10123}
10124
10125
10126# Create the Gtk2::ProgressBar to normalize
10127$pbar_n = Gtk2::ProgressBar->new;
10128$pbar_n->set_size_request( 420, 24 ); # width, height
10129#$table_down->attach_defaults ($pbar_n, 1, 4, 1, 2);
10130$table_down->attach($pbar_n,  1, 4, 1, 2, ['expand','fill'], 'shrink', 0, 0);
10131$pbar_n->set_orientation('left_to_right');
10132$pbar_n->show;
10133
10134my $da_arrow_enc = Gtk2::DrawingArea->new;
10135# set a minimum size
10136$da_arrow_enc->set_size_request (76, 20);
10137$table_down->attach ($da_arrow_enc, 0, 1, 2, 3, 'fill', 'shrink', 0, 0);
10138$da_arrow_enc->signal_connect (expose_event => \&draw_arrow_enc);
10139$da_arrow_enc->show;
10140
10141sub draw_arrow_enc {
10142   my ($widget, $event, @data) = @_;   # $widget = $da = Gtk2::DrawingArea
10143
10144   # to expose this event
10145   draw_input_output($da_arrow_enc,undef,("wav","$extension_output"));
10146
10147   return $true;
10148}
10149
10150# Create the Gtk2::ProgressBar to encode
10151$pbar_encode = Gtk2::ProgressBar->new;
10152$pbar_encode->set_size_request( 420, 24 ); # width, height
10153#$table_down->attach_defaults ($pbar_encode, 1, 4, 2, 3);
10154$table_down->attach($pbar_encode,  1, 4, 2, 3, ['expand','fill'], 'shrink', 0, 0);
10155$pbar_encode->set_orientation('left_to_right');
10156$pbar_encode->show;
10157
10158
10159##-----------------------------------------------------##
10160## ----------------- hbox + TextView ----------------- ##
10161# output command : debug textview
10162$vbox_text = new Gtk2::VBox;
10163$frame_debug->add( $vbox_text );
10164$vbox_text->set_border_width(2);
10165$vbox_text->show;
10166
10167# on hbox we put a button_clear_debug
10168$hbox_text = new Gtk2::HBox;
10169$vbox_text->pack_start( $hbox_text, $false, $false, 0 );
10170$hbox_text->show;
10171
10172# Create a centering alignment object;
10173$align = Gtk2::Alignment->new(0.0, 0.5, 0, 0);
10174$hbox_text->add($align);
10175$align->show;
10176
10177# debug clear button
10178$button_clear = Gtk2::Button->new;
10179$button_clear->add( DrawIcons('gtk-clear','button') );
10180$tooltips->set_tip( $button_clear, $langs{tip122} ) if $show_all_tooltips;
10181$button_clear->signal_connect( "clicked", \&textview_clear);
10182$button_clear->set( 'focus-on-click' => $false, 'relief' => 'none' );
10183$align->add($button_clear);
10184$button_clear->show;
10185
10186sub textview_clear {
10187       my $buffer = $textview->get_buffer;
10188       # delete buffer   -  $buffer->delete ($start, $end)
10189       # " (start, end) = $buffer->get_bounds  -  Retrieves the first and
10190       # last iterators in the buffer, i.e. the entire buffer lies within
10191       # the range (start,end). "
10192       $textview->get_buffer->delete( $buffer->get_bounds );
10193       # $iter = $buffer->get_iter_at_offset (0); # get start of buffer
10194}
10195
10196##--------------------TextView-------------------------##
10197
10198# Construct an command output text
10199$textview = new Gtk2::TextView;
10200$textview->set_wrap_mode('char'); # 'none', char, word
10201# or $textview->set_editable($false);
10202$textview->set (editable => $false, cursor_visible => $false);
10203$textview->show;
10204
10205$scrolled = new Gtk2::ScrolledWindow( undef, undef );
10206$scrolled->set_policy( 'automatic', 'always' );  # 'never', 'automatic'
10207$scrolled->set_size_request( -1, 130 );
10208$scrolled->add($textview);
10209$scrolled->set_shadow_type('GTK_SHADOW_IN'); # 'in' , 'GTK_SHADOW_IN'
10210
10211$vbox_text->add($scrolled);
10212$scrolled->show;
10213
10214# see <man Gtk2::TextBuffer>
10215# $buffer = Gtk2::TextBuffer->new;
10216$buffer = $textview->get_buffer;
10217$buffer->create_tag ("italic",       'foreground' => "red",
10218                                    #'background' => "yellow",
10219                                    justification => 'center',
10220                                            style => 'italic');
10221$buffer->create_tag ("small",        'foreground' => "blue",
10222                                    'size-points' => "10");
10223$buffer->create_tag ("small-green",  'foreground' => "green",
10224                                    justification => 'center',
10225                                    'size-points' => "10");
10226$buffer->create_tag ("small-black",  'foreground' => "black",
10227                                    justification => 'center',
10228                                    'size-points' => "10");
10229$buffer->create_tag ("black",        'foreground' => "black",
10230                                    justification => 'left',
10231                                    'size-points' => "10");
10232$buffer->create_tag ("purple",       'foreground' => "purple",
10233                                    justification => 'left',
10234                                    'size-points' => "10");
10235$buffer->create_tag ("red",          'foreground' => "red",
10236                                    justification => 'left',
10237                                    'size-points' => "11");
10238$buffer->create_tag ("small-red",    'foreground' => "red",
10239                                    justification => 'center',
10240                                    'size-points' => "10");
10241$buffer->create_tag ("bg_yellow",    'foreground' => "blue",
10242                                     'background' => "yellow",
10243                                    'size-points' => "11");
10244$buffer->create_tag ("small-ini",  'foreground' => "red",
10245                                    justification => 'left',
10246                                    'size-points' => "20");
10247
10248# "get start of buffer; each insertion will revalidate the
10249# iterator to point to just after the inserted text."
10250$iter = $buffer->get_iter_at_offset (0);
10251$buffer->insert_with_tags_by_name ($iter, "gnormalize", "italic");
10252$buffer->insert_with_tags_by_name ($iter, " $VERSION \n\n" , "small");
10253#$buffer->insert ($iter , " - author: $AUTHOR");
10254
10255# see /usr/share/doc/perl-Gtk2-1.054/examples/thread_usage.pl
10256# see <man Gtk2::TextIter>, <man Gtk2::Gdk::Threads>
10257
10258sub insert_msg {    # scrolls down the text in the textboxes
10259        my $msg = shift;
10260	my $tag_name = shift;
10261	# (start, end) = $buffer->get_bounds
10262	$buffer = $textview->get_buffer;
10263	$iter = $buffer->get_end_iter;  # or $iter = $buffer->get_bounds;
10264	# $iter = $textview->get_buffer->get_end_iter;
10265	if ($tag_name) {
10266                $buffer->insert_with_tags_by_name($iter, $msg, $tag_name);}
10267        else {  $buffer->insert($iter, $msg);} # if don't have tag
10268
10269	return if ( $button_info_type != 2 ); # if debug button is not active, return
10270	#$text_view->scroll_to_iter ($iter, $within_margin,$use_align, $xalign, $yalign)
10271	$textview->scroll_to_iter($iter, 0.0, $true, 0.00, 1.00) if $iter;
10272	while (Gtk2->events_pending()) { Gtk2->main_iteration()}; # to update
10273}
10274
10275# only for illustration
10276sub get_text {
10277	my $buffer = shift->get_buffer;
10278	$buffer->get_text ($buffer->get_start_iter, $buffer->get_end_iter, $true);
10279	# print (get_text($textview) );
10280}
10281
10282#####--------------------------------------------------#####
10283#####------------------ Subroutines -------------------#####
10284
10285sub button_with_4_phases {
10286   my ( $self, $adjust ) = @_;
10287
10288   $button_info_type += $adjust;   # set the new value; $adjust = 0 or 1.
10289   #print "button_info_type = $button_info_type ; adjust = $adjust\n";
10290
10291   $align_hold_2_buttons->remove ($align_hold_2_buttons->child);  # always remove one button
10292
10293   if ( $button_info_type == 1 ){
10294      $frame_prog_bar->show;       # show the Progress_Bar Frame
10295      $align_hold_2_buttons->add ($button_show_info);
10296      $tooltips->set_tip( $button_show_info, $langs{tip119} ) if $show_all_tooltips;
10297   }
10298   elsif ( $button_info_type == 2 ){ # $frame_debug->show;
10299      $align_hold_2_buttons->add ($button_hide_info);
10300      $tooltips->set_tip( $button_hide_info, $langs{tip120} ) if $show_all_tooltips;
10301      resize_window();
10302   }
10303   elsif ( $button_info_type == 3 ){
10304      $align_hold_2_buttons->add ($button_hide_info);
10305      $tooltips->set_tip( $button_hide_info, $langs{tip118} ) if $show_all_tooltips;
10306      resize_window();
10307   }
10308   elsif ( $button_info_type == 4 or $button_info_type == 0 ){
10309      $frame_prog_bar->hide;
10310      $align_hold_2_buttons->add ($button_show_info);
10311      $tooltips->set_tip( $button_show_info, $langs{tip117} ) if $show_all_tooltips;
10312      $button_info_type = 0; # return to inicial value
10313   }
10314}
10315button_with_4_phases(0,0); # '0' to not change the $button_info_type value
10316
10317
10318# Show or hide the frame_debug that show some output commands,
10319# and hold the notebook size constant without shrink.
10320sub resize_window {
10321    my $width  = $window->allocation->width;    # get main window width
10322    my $height = $window->allocation->height;   # get main window height
10323
10324    # get $frame_debug height ; for the first time, always '$frame_debug->allocation->height < 10' is true
10325    my $height_fd = $frame_debug->allocation->height;
10326    $height_fd = $frame_debug->allocation->height < 10 ? 177 : $height_fd;  # 177 pixel is the minimum size
10327
10328    #$vpaned->child1_resize ($false); # "child1_resize" determines whether the first child should expand when $paned is resized.
10329    #print "\nfora:     width = $width ; height = $height ; height_fd = ",$frame_debug->allocation->height,"\n";
10330
10331    if ( $button_info_type == 2 ){
10332       my $pos = $vpaned->get_position;
10333       $window->resize ( $width, $height + $height_fd ); # ($width, $height)
10334       $frame_debug->show;
10335       $vpaned->set_position ($pos);
10336       #print "-->ativo: width = $width ; height = ",$height + $height_fd," ; height_fd = $height_fd\n";
10337    }
10338    elsif ( $button_info_type == 3 ){
10339       $frame_debug->hide;
10340       return if $frame_debug->allocation->height < 10;
10341       $window->resize ( $width, $height - $height_fd );
10342       #print "desativo: width = $width ; height = ",$height - $height_fd," ; height_fd = ",$frame_debug->allocation->height,"\n";
10343       #$vpaned->set_position ($height);
10344    }
10345    #$vpaned->compute_position ($allocation, $child1_req, $child2_req);
10346    $vpaned->compute_position (0, $vbox_up->allocation->height, $vbox_down->allocation->height);
10347    #$vpaned->child1_resize ($true);
10348
10349    # boolean = $widget->get_child_visible
10350    #if ($frame_debug->get_child_visible){print "oooi\n";}
10351}
10352
10353####----------------------------------------------------####
10354
10355#####---------------- Programs Path -------------------#####
10356
10357# sub inspirated from normalize
10358sub find_prog {  # Find a valid path for the program
10359
10360    # See Perl Cookbook, 2nd Edition, Ch. 10.7
10361    my %args = (         # default value
10362                Progs    => undef,          # array with program name: name1::name2::name3:: ... = (name1,name2,name3,...)
10363		Comment  => "",             # show one message/comment
10364                @_,      # argument pair list goes here
10365	       );
10366    my @progs = split(/::/, $args{Progs});  # an Array for elements in hash // Odd number of elements in hash assignment
10367    my $path = undef;
10368    my $fullpath;
10369    my $prog;
10370    #foreach $prog (@progs){ print "\$prog = $prog\n"; }
10371
10372    @_ = split(/:/, $ENV{PATH});       # get the system executable paths
10373
10374    LOOP:foreach $prog (@progs){
10375        for (@_) {                     # $_ is an element of @_
10376	   ($_ .= "/") unless (/\/$/); # put a "/" at the end of path
10377	   # print " S = $_ \n";
10378	   $fullpath = $_.$prog;
10379	   if (-x $fullpath) {         # if (-x file) : file is executable by effective uid/gid.
10380	      $path = $fullpath;
10381	      last LOOP;               # abandon the 'foreach' LOOP
10382	   }
10383        }
10384    }
10385    if ( $args{Comment} eq "NO_COMMENT" ) { return $path; }
10386
10387    foreach $prog (@progs){
10388       if ( not defined $path ) {
10389          insert_msg( $langs{msg023}, "black");
10390          insert_msg("$prog" , "red");
10391          insert_msg( $langs{msg024}." $args{Comment}\n" , "black");
10392       }
10393    }
10394    return $path;
10395}
10396
10397
10398sub verify_all_paths {
10399    $os = exec_cmd_system( 'uname -s' ); # operational system name: $os = Linux
10400
10401    # see <man Gtk2::version>
10402    # the GtkFileChooser, new in gtk+ 2.6.0 and first supported in
10403    # Gtk2-Perl at 1.100, is available.
10404    my $Gtk2_version_boolean = Gtk2->CHECK_VERSION (2, 6, 0); # boolean
10405    my ($Gtk2_version_MAJOR, $Gtk2_version_MINOR, $Gtk2_version_MICRO) = Gtk2->GET_VERSION_INFO;
10406    my $Gtk2_version = $Gtk2_version_MAJOR.".".$Gtk2_version_MINOR.".".$Gtk2_version_MICRO;
10407    my $Gtk2_Perl_version = $Gtk2::VERSION;
10408
10409    if ( $Gtk2_Perl_version < 1.100 or not $Gtk2_version_boolean ) {
10410       insert_msg( "Found Gtk2 (version = $Gtk2_version) and Gtk2_Perl (version = $Gtk2_Perl_version).", "black");
10411       insert_msg( "\n$langs{msg079}\n" , "red");
10412       insert_msg( "   Gtk2 version >= 2.6.0 ; Gtk2_Perl version >= 1.100\n\n", "red");
10413    }
10414
10415    $lame_path = find_prog( Progs => 'lame', Comment => $langs{msg025} );
10416    #print " lame_path = $lame_path \n";
10417    unless ($lame_path) { $button_output_mp3->set_sensitive($false); }
10418
10419    $oggenc_path = find_prog( Progs => "oggenc", Comment => $langs{msg026} );
10420    #print " oggenc_path = $oggenc_path \n";
10421    unless ($oggenc_path) { $button_output_ogg->set_sensitive($false); }
10422
10423    $mpcenc_path = find_prog( Progs => "mppenc", Comment => $langs{msg027} );
10424    unless ($mpcenc_path) { $button_output_mpc->set_sensitive($false);}
10425
10426    $ape_path = find_prog( Progs => "mac", Comment => $langs{msg028} );
10427    unless ($ape_path) { $button_output_ape->set_sensitive($false);  }
10428
10429    $flac_path = find_prog( Progs => "flac", Comment => $langs{msg029} );
10430    unless ($flac_path) { $button_output_flac->set_sensitive($false);}
10431
10432    $faac_path = find_prog( Progs => "faac", Comment => $langs{msg030} );
10433    unless ($faac_path) { $button_output_mp4->set_sensitive($false);}
10434
10435    if (not defined $lame_path   and $extension_output eq 'mp3') {$extension_output = 'ogg'; }
10436    if (not defined $oggenc_path and $extension_output eq 'ogg') {$extension_output = 'mpc'; }
10437    if (not defined $mpcenc_path and $extension_output eq 'mpc') {$extension_output = 'ape'; }
10438    if (not defined $ape_path    and $extension_output eq 'ape') {$extension_output = 'flac';}
10439    if (not defined $flac_path   and $extension_output eq 'flac'){$extension_output = 'mp4'; }
10440    if (not defined $faac_path   and $extension_output eq 'mp4') {$extension_output = 'wav'; }
10441
10442    if    ($extension_output eq 'mp3'  and $lame_path   ) {$button_output_mp3->set_active( $true ); }  #padr�o - ativo
10443    elsif ($extension_output eq 'ogg'  and $oggenc_path ) {$button_output_ogg->set_active( $true ); }
10444    elsif ($extension_output eq 'mpc'  and $mpcenc_path ) {$button_output_mpc->set_active( $true ); }
10445    elsif ($extension_output eq 'ape'  and $ape_path    ) {$button_output_ape->set_active( $true ); }
10446    elsif ($extension_output eq 'flac' and $flac_path   ) {$button_output_flac->set_active( $true );}
10447    elsif ($extension_output eq 'mp4'  and $faac_path   ) {$button_output_mp4->set_active( $true ); }
10448    elsif ($extension_output eq 'wav') {$button_output_wav->set_active( $true );}
10449    else  {$normalize_button->set_sensitive($false);} # do nothing!!
10450
10451    $metaflac_path = find_prog( Progs => "metaflac");
10452
10453    # 'normalize-audio' is a link used on Ubuntu Linux to 'normalize'
10454    # $normalize_path = find_prog( Progs => 'normalize::normalize-audio', Comment => $langs{msg031} );
10455
10456    # 'gnormalize-wavegain' is a link used on Suse Linux to 'wavegain'
10457    $wavegain_path = find_prog( Progs => 'wavegain::gnormalize-wavegain', Comment => $langs{msg031} );
10458    unless ($wavegain_path) {
10459       $norm_type = "None";
10460       $ComboBox_Norm_Type->set_active(2); # 2 set to "None"
10461       $ComboBox_Norm_Type->set_sensitive($false);
10462       $spinner->set_sensitive($false);
10463    }
10464
10465    $oggdec_path = find_prog( Progs => "oggdec", Comment => $langs{msg032} );
10466
10467    $mpcdec_path = find_prog( Progs => "mppdec", Comment => $langs{msg033} );
10468
10469    $faad_path = find_prog( Progs => "faad", Comment => $langs{msg034} );
10470
10471    $cdparanoia_path = find_prog( Progs => "cdparanoia");
10472    unless ($cdparanoia_path) {
10473	$ripper = "cdda2wav";
10474	ComboBox_select_this_text(\$ComboBox_rip,\@rippers,\$ripper);
10475	$ComboBox_rip->set_sensitive($false);
10476    }
10477    $cdda2wav_path = find_prog( Progs => "cdda2wav", Comment => "NO_COMMENT" );
10478    unless ($cdda2wav_path) {
10479	$ripper = "cdparanoia";
10480	ComboBox_select_this_text(\$ComboBox_rip,\@rippers,\$ripper);
10481	$ComboBox_rip->set_sensitive($false);
10482    }
10483
10484    $vorbiscomment_path = find_prog( Progs => "vorbiscomment");
10485
10486    if (not $use_external_cddbget){ insert_msg( "$langs{msg082}\n", "black"); }
10487    if (not $use_external_MP3Info){ insert_msg( "$langs{msg083}\n", "black"); }
10488
10489    #---- mp3 players ----# ;
10490    $madplay_path = find_prog( Progs => "madplay", Comment => "NO_COMMENT" );
10491    $mpg123_path  = find_prog( Progs => "mpg123",  Comment => "NO_COMMENT" );
10492    $mpg321_path  = find_prog( Progs => "mpg321",  Comment => "NO_COMMENT" );
10493    $mplayer_path = find_prog( Progs => "mplayer", Comment => "NO_COMMENT" );
10494
10495    $all_mp3_player{"mpg321"}  = $true if $mpg321_path;
10496    $all_mp3_player{"mpg123"}  = $true if $mpg123_path;
10497    $all_mp3_player{"madplay"} = $true if $madplay_path;
10498    $all_mp3_player{"mplayer"} = $true if $mplayer_path;
10499
10500    foreach my $key (sort keys %all_mp3_player) {
10501       $player_mp3 = $key if ( not $all_mp3_player{"$player_mp3"} );
10502       #print "The value of <$key> is <$all_mp3_player{$key}> ;; player_mp3 = $player_mp3\n";
10503    }
10504
10505    if ( not %all_mp3_player ){ # no mp3 player was found
10506       find_prog( Progs => "mpg123::mpg321::madplay::mplayer", Comment => "MP3 files can't be played!" );
10507    }
10508
10509    #---- mp4 players ----#
10510    $all_mp4_player{"mplayer"} = $true if $mplayer_path;
10511
10512    foreach my $key (sort keys %all_mp4_player) {
10513       $player_mp4 = $key if ( not $all_mp4_player{"$player_mp4"} );
10514    }
10515
10516    #---- mpc players ----#
10517    $all_mpc_player{"mplayer"} = $true if $mplayer_path;
10518    $all_mpc_player{"mpcdec"}  = $true  if $mpcdec_path;
10519
10520    foreach my $key (sort keys %all_mpc_player) {
10521       $player_mpc = $key if ( not $all_mpc_player{"$player_mpc"} );
10522    }
10523
10524    if ( not %all_mpc_player ){ # no mpc player was found
10525       find_prog( Progs => "mpcdec::mplayer", Comment => "MPC files can't be played!" );
10526    }
10527
10528    #---- ogg players ----# ;
10529    $ogg123_path  = find_prog( Progs => "ogg123", Comment => "NO_COMMENT" );
10530
10531    $all_ogg_player{"mplayer"} = $true if $mplayer_path;
10532    $all_ogg_player{"ogg123"}  = $true if $ogg123_path;
10533
10534    foreach my $key (keys %all_ogg_player) {
10535       $player_ogg = $key if ( not $all_ogg_player{"$player_ogg"} );
10536    }
10537
10538    if ( not %all_ogg_player ){ # no ogg player was found
10539       find_prog( Progs => "ogg123::mplayer", Comment => "OGG files can't be played!" );
10540    }
10541
10542    #---- ape players ----#
10543    $play_path  = find_prog( Progs => "play",  Comment => "NO_COMMENT" ); # play from sox
10544    $aplay_path = find_prog( Progs => "aplay", Comment => "NO_COMMENT" );
10545
10546    $all_ape_player{"mplayer"} = $true if $mplayer_path;
10547    $all_ape_player{"mac"}     = $true if ($ape_path and $play_path);
10548    $all_ape_player{"aplay"}   = $true if ($ape_path and $aplay_path);
10549
10550    foreach my $key (keys %all_ape_player) {
10551       $player_ape = $key if ( not $all_ape_player{"$player_ape"} );
10552    }
10553
10554    unless ( %all_ape_player ){ # no ape player was found
10555       find_prog( Progs => "mac::play::aplay::mplayer", Comment => "APE files can't be played!" );
10556    }
10557
10558    #---- flac players ----#
10559    $flac123_path = find_prog( Progs => "flac123", Comment => "NO_COMMENT" );
10560
10561    $all_flac_player{"mplayer"} = $true if $mplayer_path;
10562    $all_flac_player{"flac123"} = $true if $flac123_path;
10563
10564    foreach my $key (sort keys %all_flac_player) {
10565       $player_flac = $key if ( not $all_flac_player{"$player_flac"} );
10566    }
10567
10568    if ( not %all_flac_player ){ # no flac player was found
10569       find_prog( Progs => "flac123::mplayer", Comment => "FLAC files can't be played!" );
10570    }
10571
10572    #---- wav players ----#
10573    $all_wav_player{"mplayer"} = $true if $mplayer_path;
10574    $all_wav_player{"aplay"}   = $true if $aplay_path;
10575    $all_wav_player{"play"}    = $true if $play_path;
10576
10577    foreach my $key (sort keys %all_wav_player) {
10578       $player_wav = $key if ( not $all_wav_player{"$player_wav"} );
10579    }
10580
10581    if ( not %all_wav_player ){ # no wav player was found
10582       find_prog( Progs => "mplayer::aplay::play", Comment => "WAV files can't be played!" );
10583    }
10584
10585    #---- ----------- ----#
10586
10587    $amixer_path = find_prog( Progs => "amixer", Comment => "Can not set volume level!" );
10588    $nice_path   = find_prog( Progs => "nice", Comment => "Can not modify scheduling priority!" );
10589}
10590
10591sub cdplayer_available { # Audio CD player uses gnormalize::cdplay or Audio::CD or cdcd.
10592   my @cdplayers = ();
10593   $cdcd_path = find_prog( Progs => "cdcd", Comment => "NO_COMMENT" );
10594
10595   if ( $cdcd_path and not -e filename_from_unicode "$home/.cdcdrc" ){
10596      insert_msg( "\n$langs{msg036}\n" , "black");
10597      insert_msg( "$langs{msg037}\n" , "black");
10598   }
10599   if ( $use_audiocd eq $false and not defined $cdcd_path and $os !~ /Linux/i ){
10600      insert_msg( $langs{msg038} , "black");
10601      insert_msg("Audio::CD" , "red");
10602      insert_msg("\".\n" , "black");
10603      $cdcd_path = find_prog( Progs => "cdcd", Comment => $langs{msg035} );
10604   }
10605
10606   # $cdplayer = "Audio::CD" or "cdcd" or "gnormalize::cdplay"
10607   if ($os =~ /Linux/i       ) { push @cdplayers, "gnormalize::cdplay";}
10608   if ($use_audiocd eq $true ) { push @cdplayers, "Audio::CD";  }
10609   if ($cdcd_path            ) { push @cdplayers, "cdcd";       }
10610
10611   return @cdplayers;
10612}
10613
10614verify_all_paths();
10615@all_cdplayer = cdplayer_available();
10616Make_ComboBox_cdplayer();
10617
10618
10619#####-------------- Output Directory Selection --------------#####
10620#####------------------------ start -------------------------#####
10621
10622my $file_dialog_output;
10623sub dir_selection_output {
10624   # repair the standard cancel label
10625   my @items = ( { stock_id => "gtk-cancel", label => "_Cancel"     } );
10626   Gtk2::Stock->add (@items);  # Register our stock items
10627
10628   $file_dialog_output = Gtk2::FileChooserDialog->new( "Choose the Output Directory" ,
10629                                                       undef, 'open',
10630					               'gtk-cancel' => 'cancel',
10631                                                       'gtk-ok' => 'ok');
10632
10633   # On Unix, the assumption of GLib and GTK+ by default is that filenames on
10634   # the filesystem are encoded in UTF-8 rather than the encoding of the locale; (Release notes for GTK+ 2.6)
10635   # Output = gtk_func->set_text(Input), Output and Input should be in unicode on the perl level.
10636
10637   # filename_to_unicode  : convert local encoding --> unicode.
10638   # filename_from_unicode: convert unicode --> local encoding.
10639
10640   # filename_display_name is used with filename that need to display in gtk+ and guaranteed
10641   # to return valid utf8, but the conversion is not necessary reversible. See 'man Glib'.
10642
10643   #_utf8_on($directory_output); # Turn on the UTF8 flag so Perl don't try to convert the some "$string1$string2" to utf8.
10644
10645   #print "\n dir_selection_output --> 1\$directory_output = ", $directory_output,"\n";
10646   #$directory_output = encode('utf8',$directory_output);
10647   my $current_folder = $entry_dir_output->get_text;  # '$entry_dir_output->get_text' are in unicode reversible
10648   $current_folder    = $home if not -d filename_from_unicode $current_folder;
10649
10650   # "you first need to convert filename from the local encoding to unicode." (see <man Glib>)
10651   $file_dialog_output->set_current_folder(  filename_from_unicode $current_folder ); #abrir no lacal espec�fico
10652
10653   # Gtk2::FileChooserAction : 'select-folder', 'open',
10654   $file_dialog_output->set_action ('select-folder'); # directory selection only
10655   if ($file_dialog_output->run eq 'ok') {
10656
10657      $directory_output = $file_dialog_output->get_filename;               # '$file_dialog_output->get_filename' are in unicode reversible
10658      eval { $directory_output = filename_to_unicode $directory_output; }; # To remove Wide character
10659
10660      verify_writable_dir( $directory_output );
10661
10662      $entry_dir_output->set_text( $directory_output );
10663      $tooltips->set_tip( $entry_dir_output, $directory_output ) if $show_all_tooltips;
10664
10665      #print "\n dir_selection_output --> 2\$directory_output = ", $directory_output,"\n";
10666      insert_msg("\n dir_selection_output --> \$directory_output = $directory_output\n" , "small"); #print on debug textview
10667   };
10668   $file_dialog_output->destroy;
10669}
10670
10671sub verify_writable_dir {
10672   my $dir = shift;
10673
10674   if ( not -w filename_from_unicode $dir ){
10675         $status_bar->push($context_id, " $dir ".$langs{msg012} );
10676	 $directory_output = $home;
10677	 $entry_dir_output->set_text( $directory_output );
10678         $tooltips->set_tip( $entry_dir_output, $directory_output ) if $show_all_tooltips;
10679   }
10680   else{ $status_bar->push($context_id, " " ); }
10681}
10682
10683#####------------------------ final -------------------------#####
10684#####-------------- Output Directory Selection --------------#####
10685
10686
10687#####------------------- Directory Selection ----------------#####
10688#####------------------------ start -------------------------#####
10689
10690sub dir_selection {
10691
10692   # repair the standard cancel label
10693   my @items = ( { stock_id => "gtk-cancel", label => "_Cancel"     } );
10694   Gtk2::Stock->add (@items);  # Register our stock items
10695
10696   my $fd_msg = $langs{msg087};
10697
10698   # Create a new file selection widget
10699   # See file:///usr/share/gtk-doc/html/gtk/GtkFileChooserDialog.html,
10700   # /usr/share/doc/perl-Gtk2-1.080/examples/file_chooser.pl
10701   # or <man Gtk2::FileChooserDialog> and <man Gtk2::FileChooser>
10702   # clicking the 'gtk-cancel' button will emit the "response" signal with the given response_id.
10703   $file_dialog = Gtk2::FileChooserDialog->new( $fd_msg ,
10704                                                undef, 'open',
10705					        'gtk-cancel' => 'cancel',
10706                                                'gtk-ok' => 'ok');
10707
10708   ###----------------------- Filters -----------------------###
10709   # Unfortunately Gtk2::FileFilter don't have the 'insensitive case' option!!!
10710
10711   my $filter_all = Gtk2::FileFilter->new;
10712   $filter_all->set_name ('All files');
10713   $filter_all->add_pattern ('*');
10714   $file_dialog->add_filter ($filter_all);
10715
10716   my $filter_mp3 = Gtk2::FileFilter->new;
10717   $filter_mp3->set_name ('.mp3'); # 2 ** 2 = 4 combinations
10718   $filter_mp3->add_pattern ('*.mp3');   $filter_mp3->add_pattern ('*.mP3');
10719   $filter_mp3->add_pattern ('*.Mp3');   $filter_mp3->add_pattern ('*.MP3');
10720   $file_dialog->add_filter ($filter_mp3);
10721
10722   my $filter_mp4 = Gtk2::FileFilter->new;
10723   $filter_mp4->set_name ('.mp4'); # All 2 ** 2 = 4 combinations for mp4
10724   $filter_mp4->add_pattern ('*.mp4');   $filter_mp4->add_pattern ('*.mP4');
10725   $filter_mp4->add_pattern ('*.Mp4');   $filter_mp4->add_pattern ('*.MP4');
10726   $filter_mp4->add_pattern ('*.M4A');   $filter_mp4->add_pattern ('*.m4a'); # for m4a
10727   $filter_mp4->add_pattern ('*.M4a');   $filter_mp4->add_pattern ('*.m4A');
10728   $filter_mp4->add_pattern ('*.aac');   $filter_mp4->add_pattern ('*.AAC'); # for aac
10729   $file_dialog->add_filter ($filter_mp4);
10730
10731   my $filter_mpc = Gtk2::FileFilter->new;
10732   $filter_mpc->set_name ('.mpc');
10733   $filter_mpc->add_pattern ('*.mpc');   $filter_mpc->add_pattern ('*.MPC');
10734   $filter_mpc->add_pattern ('*.mpp');   $filter_mpc->add_pattern ('*.MPP');
10735   $filter_mpc->add_pattern ('*.mp+');   $filter_mpc->add_pattern ('*.MP+');
10736   $file_dialog->add_filter ($filter_mpc);
10737
10738   my $filter_ogg = Gtk2::FileFilter->new;
10739   $filter_ogg->set_name ('.ogg'); # All 2 ** 3 = 8 combinations
10740   $filter_ogg->add_pattern ('*.ogg');   $filter_ogg->add_pattern ('*.Ogg');
10741   $filter_ogg->add_pattern ('*.oGg');   $filter_ogg->add_pattern ('*.ogG');
10742   $filter_ogg->add_pattern ('*.OGg');   $filter_ogg->add_pattern ('*.OgG');
10743   $filter_ogg->add_pattern ('*.oGG');   $filter_ogg->add_pattern ('*.OGG');
10744   $file_dialog->add_filter ($filter_ogg);
10745
10746   my $filter_ape = Gtk2::FileFilter->new;
10747   $filter_ape->set_name ('.ape'); # There are 2 ** 3 = 8 combinations
10748   $filter_ape->add_pattern ('*.ape');   $filter_ape->add_pattern ('*.APE');
10749   $filter_ape->add_pattern ('*.Ape');   $filter_ape->add_pattern ('*.aPe');
10750   $filter_ape->add_pattern ('*.apE');   $filter_ape->add_pattern ('*.APe');
10751   $filter_ape->add_pattern ('*.aPE');   $filter_ape->add_pattern ('*.aPe');
10752   $file_dialog->add_filter ($filter_ape);
10753
10754   my $filter_flac = Gtk2::FileFilter->new;
10755   $filter_flac->set_name ('.flac'); # There are 2 ** 4 = 16 combinations
10756   $filter_flac->add_pattern ('*.flac');   $filter_flac->add_pattern ('*.FLAC');
10757   $filter_flac->add_pattern ('*.Flac');   $filter_flac->add_pattern ('*.fLac');
10758   $filter_flac->add_pattern ('*.flAc');   $filter_flac->add_pattern ('*.flaC');
10759   $filter_flac->add_pattern ('*.FLac');   $filter_flac->add_pattern ('*.FlAc');
10760   $filter_flac->add_pattern ('*.FlaC');   $filter_flac->add_pattern ('*.fLAc');
10761   $filter_flac->add_pattern ('*.fLaC');   $filter_flac->add_pattern ('*.flAC');
10762   $filter_flac->add_pattern ('*.FLAc');   $filter_flac->add_pattern ('*.FLaC');
10763   $filter_flac->add_pattern ('*.FlAC');   $filter_flac->add_pattern ('*.fLAC');
10764
10765   $file_dialog->add_filter ($filter_flac);
10766
10767   my $filter_wav = Gtk2::FileFilter->new;
10768   $filter_wav->set_name ('.wav'); # There are 2 ** 3 = 8 combinations
10769   $filter_wav->add_pattern ('*.wav');   $filter_wav->add_pattern ('*.WAV');
10770   $filter_wav->add_pattern ('*.Wav');   $filter_wav->add_pattern ('*.waV');
10771   $filter_wav->add_pattern ('*.wAv');   $filter_wav->add_pattern ('*.WAv');
10772   $filter_wav->add_pattern ('*.WaV');   $filter_wav->add_pattern ('*.wAV');
10773   $file_dialog->add_filter ($filter_wav);
10774
10775   ###----------------------- Filters -----------------------###
10776   my $current_folder = $entry_dir_input->get_text;                           # '$entry_dir_output->get_text' are in unicode reversible
10777   $current_folder    = $home if not -d filename_from_unicode $current_folder;
10778
10779   $file_dialog->set_current_folder( filename_from_unicode $current_folder ); # to open at specific local
10780
10781   # Gtk2::FileChooserAction : 'select-folder', 'open',
10782   $file_dialog->set_action ('select-folder'); # directory selection only
10783
10784   $check_button_recursively = Gtk2::CheckButton->new ( $langs{name008} );  #16jun2008
10785   $check_button_recursively->set_active($recursively);
10786   $check_button_recursively->signal_connect( "clicked", sub { $recursively = $check_button_recursively->get_active? 1 : 0 ; } );
10787   $tooltips->set_tip( $check_button_recursively, $langs{tip107} ) if $show_all_tooltips;
10788   $check_button_recursively->show;
10789
10790   # Create the label for "set_extra_widget".
10791   $label_pct = Gtk2::Label->new("0.0%");
10792   $label_pct->show;
10793
10794   # Create the Gtk2::ProgressBar to read file info for "set_extra_widget".
10795   $progbar_files_info = Gtk2::ProgressBar->new;
10796   $progbar_files_info->set_text( $directory_base );
10797   $progbar_files_info->set_orientation('left_to_right');
10798   $progbar_files_info->show;
10799
10800   # Add  $label_pct and  $progbar_files_info into  $vbox_extra_widget
10801   my $vbox_extra_widget = Gtk2::VBox->new( $false, 8 );
10802   $vbox_extra_widget->add($check_button_recursively);
10803   $vbox_extra_widget->add($label_pct);
10804   $vbox_extra_widget->add($progbar_files_info);
10805   $vbox_extra_widget->show;
10806
10807   # Add  $vbox_extra_widget into $file_dialog
10808   $file_dialog->set_extra_widget ($vbox_extra_widget);
10809
10810   $file_dialog->signal_connect( "selection-changed", sub {
10811      my $selected_dir = $file_dialog->get_filename; # '$file_dialog->get_filename' is in local encoding ??
10812      eval { $selected_dir = filename_to_unicode $selected_dir; };
10813
10814      $file_dialog->set_show_hidden ($false);
10815      # $file_dialog->set('use-preview-label' => $true );
10816
10817      $entry_dir_input->set_text($selected_dir);
10818      $progbar_files_info->set_text( $selected_dir );
10819      $tooltips->set_tip( $entry_dir_input, $selected_dir) if $show_all_tooltips;
10820      # print "\$selected_dir = $selected_dir\n";
10821   });
10822
10823   if ( $file_dialog->run eq 'ok' ) {
10824
10825      # $directory_base is used to find 'directory_ramain' with remove_directory_base()
10826      $directory_base = $file_dialog->get_filename;                     # '$file_dialog_output->get_filename' are in unicode reversible
10827      eval { $directory_base = filename_to_unicode $directory_base; };  # To remove Wide character
10828
10829      my $msg = ($check_button_recursively->get_active) ? $langs{msg086}: $langs{msg088};
10830      insert_msg( "\n$msg: $directory_base\n\n" , "small");
10831
10832      #print "\n dir_selection --> \$directory_base = $directory_base\n";
10833      fill_with_all_informations($directory_base);
10834   };
10835   $file_dialog->destroy;
10836}
10837
10838#####------------------------ final -------------------------#####
10839#####------------------- Directory Selection ----------------#####
10840
10841sub fill_with_all_informations {
10842   my $dir_path = shift;  # directory
10843
10844   make_array_of_files($dir_path);  # make an array of files with only supported extensions: *.mp3 , *.mp4*, *.mpc, *.ogg ...
10845   if (@array_of_files <= 0){
10846      $tooltips->set_tip( $entry_f, $langs{msg063} ) if $show_all_tooltips;  # entry at beginning
10847      return;
10848   }
10849   #for (@array_of_files) { print "$_\n"; }
10850   fill_treeview_files_to_be_played();
10851
10852   $normalize_button->set_label("normalize");
10853   change_font_for_all_child($window_font_name,$normalize_button);
10854}
10855
10856sub determine_directory_and_filename_and_extension {  # filepath = (directory/)(filename)
10857
10858   my %args = (  @_,  # argument pair list goes here
10859	      );
10860   my $row      = $args{row};
10861   my $filepath = defined $row ? $files_info[$row]{filepath} : $args{filepath} ;
10862
10863   return unless defined $filepath;
10864
10865   $filepath                   =~ s/\/{2,}/\//g;       # change two or more '//' character for one '/'
10866   (my $filename = $filepath)  =~ s/.*\/(.*)/$1/g;     # (path/)(filename): copy and get the (filename)
10867   (my $dir      = $filepath)  =~ s/(.*\/).*/$1/g;     # (path/)(filename): copy and get the (path/)
10868   $dir                        =~ s/(\/.+)\/{1,}$/$1/; # remove the '/' character from final, if it exists, unless '/'.
10869
10870   # determine the directory and file   ;; see 'man File::Basename' and 'fileparse'
10871
10872   # determine the input/extension ;; example: $filename = 'Music.MP3' or 'Music.flac'
10873
10874   if    ( $filename =~ /\.mp3$/i            ){ $extension_input = 'mp3';  }  # for files like: music.Mp3, music.MP3  or music.mP3
10875   elsif ( $filename =~ /\.(mp4|m4a|aac)$/i  ){ $extension_input = 'mp4';  }
10876   elsif ( $filename =~ /\.(mpc|mpp|mp\+)$/i ){ $extension_input = 'mpc';  }  # for files like: music.mpc, .mpp, MPC, MPp ...
10877   elsif ( $filename =~ /\.ogg$/i            ){ $extension_input = 'ogg';  }  # for files like: music.OGG, music.OgG  or music.oGG or ...
10878   elsif ( $filename =~ /\.ape$/i            ){ $extension_input = 'ape';  }
10879   elsif ( $filename =~ /\.flac$/i           ){ $extension_input = 'flac'; }
10880   elsif ( $filename =~ /\.cda$/i            ){ $extension_input = 'cda';  }
10881   elsif ( $filename =~ /\.wav$/i            ){ $extension_input = 'wav'   }
10882   else                                       { $extension_input = 'other';}
10883
10884   # get all file extensions from original filename
10885
10886   (my $file_mp3  = $filename) =~ s/\.((.){3}|flac)$/.mp3/i; # to obtain the filename with appropriate extension_input
10887   (my $file_mp4  = $filename) =~ s/\.((.){3}|flac)$/.mp4/i; # '(.){3}' is the same as '...'
10888   (my $file_ogg  = $filename) =~ s/\.((.){3}|flac)$/.ogg/i;
10889   (my $file_mpc  = $filename) =~ s/\.(...|flac)$/.mpc/i;
10890   (my $file_ape  = $filename) =~ s/\.(...|flac)$/.ape/i;
10891   (my $file_flac = $filename) =~ s/\.(...|flac)$/.flac/i;
10892   (my $file_cda  = $filename) =~ s/\.(...|flac)$/.cda/i;
10893   (my $file_wav  = $filename) =~ s/\.(...|flac)$/.wav/i;
10894
10895   # return to the real extension_input name; for that cases *.mP3, Mp3, ...
10896   if    ( $extension_input eq 'mp3' ){ $file_mp3  = $filename; }
10897   elsif ( $extension_input eq 'mp4' ){ $file_mp4  = $filename; }
10898   elsif ( $extension_input eq 'mpc' ){ $file_mpc  = $filename; }
10899   elsif ( $extension_input eq 'ogg' ){ $file_ogg  = $filename; } # return to original extension_input
10900   elsif ( $extension_input eq 'ape' ){ $file_ape  = $filename; }
10901   elsif ( $extension_input eq 'flac'){ $file_flac = $filename; }
10902   elsif ( $extension_input eq 'cda' ){ $file_cda  = $filename; }
10903   elsif ( $extension_input eq 'wav' ){ $file_wav  = $filename; }
10904
10905   $entry_f->set_text( $filename ); # refresh file to normalize
10906   $tooltips->set_tip( $entry_f, $filename) if $show_all_tooltips;
10907   $entry_dir_input->set_text( $dir );
10908   $tooltips->set_tip( $entry_dir_input, "$dir") if $show_all_tooltips;
10909
10910   return ( row        => $row, extension_input => $extension_input,
10911	    filename   => $filename,  directory => $dir,
10912	    filepath   => $dir . '/' . $filename,
10913
10914            file_mp3   => $file_mp3,  file_mp4  => $file_mp4,  file_mpc   => $file_mpc,
10915	    file_ogg   => $file_ogg,  file_ape  => $file_ape,  file_flac  => $file_flac,
10916	    file_cda   => $file_cda,  file_wav  => $file_wav,
10917	  );
10918}
10919
10920sub make_directory_final { # when normalizing recursively or not.
10921   my %args = ( @_ );
10922   my $file_wav = $args{file_wav}; my $dir = $args{directory}; my $row = $args{row};
10923
10924   $directory_final = "$directory_output/$files_info[$row]{directory_remain}"; # where all the files will be saved
10925   $directory_final =~ s/\/{2,}/\//g; # change the '//' character for '/'
10926   $directory_final =~ s/\/{1,}$//;   # remove the '/' character from final if it exists.
10927
10928   if ( not -d filename_from_unicode $directory_final ){
10929      my @cmd = ( 'mkdir', '-p', $directory_final );
10930      insert_msg("\n@cmd\n" , "small"); #print on debug textview
10931      exec_cmd_system2(@cmd);
10932   }
10933   #print " make_directory_final --> directory = $dir \ndirectory_base = $directory_base\n";
10934   #print " make_directory_final --> directory_remain = $args{directory_remain} \ndirectory_final = $directory_final\n\n";
10935}
10936
10937sub refresh_input_output_arrows {  # draw arrows
10938
10939   if    ( $extension_input eq 'mp4' ){  draw_input_output($da_arrow_dec,undef,("mp4","wav")); }
10940   elsif ( $extension_input eq 'mpc' ){  draw_input_output($da_arrow_dec,undef,("mpc","wav")); }
10941   elsif ( $extension_input eq 'ogg' ){  draw_input_output($da_arrow_dec,undef,("ogg","wav")); }
10942   elsif ( $extension_input eq 'ape' ){  draw_input_output($da_arrow_dec,undef,("ape","wav")); }
10943   elsif ( $extension_input eq 'flac'){  draw_input_output($da_arrow_dec,undef,("flac","wav"));}
10944   elsif ( $extension_input eq 'wav' ){  draw_input_output($da_arrow_dec,undef,("wav","wav")); }
10945   elsif ( $extension_input eq 'cda' ){  draw_input_output($da_arrow_dec,undef,("cda","wav")); }
10946   else  {                               draw_input_output($da_arrow_dec,undef,("mp3","wav")); } # default
10947
10948   if ( $extension_input eq 'mp3' or $extension_input  eq 'mpc'  or
10949        $extension_input eq 'ape' or $extension_input  eq 'flac' or
10950        $extension_input eq 'wav' or ($extension_input eq 'ogg' and $vorbiscomment_path ne "") ) {
10951
10952        $status_bar->push($context_id, " $extension_input ".$langs{msg066} );
10953   }
10954}
10955
10956####--------------------------------------------------####
10957
10958sub set_extension_output {
10959
10960   if ($button_output_mp3->get_active) { $extension_output = 'mp3'; }
10961   if ($button_output_mp4->get_active) { $extension_output = 'mp4'; }
10962   if ($button_output_mpc->get_active) { $extension_output = 'mpc'; }
10963   if ($button_output_ogg->get_active) { $extension_output = 'ogg'; }
10964   if ($button_output_ape->get_active) { $extension_output = 'ape'; }
10965   if ($button_output_flac->get_active){ $extension_output = 'flac';}
10966   if ($button_output_wav->get_active) { $extension_output = 'wav'; }
10967
10968   encode_choice_sensitive();
10969
10970   my $row  = $files_info[0]{get_selection_row} || 0;
10971   my %hash = determine_directory_and_filename_and_extension(row => $row);
10972   set_output_and_refresh_progress_bar(%hash);
10973   #print "\$extension_output = $extension_output ; \$row = $row\n";
10974}
10975
10976sub set_output_and_refresh_progress_bar {
10977   my %args = ( @_,     # argument pair list goes here
10978	      );
10979   my $extension_input = $args{extension_input};
10980   my $row = $args{row};
10981
10982   return if ( not defined $extension_input or $extension_input eq 'other' );
10983
10984   my $file_mp3 = $args{file_mp3}; my $file_mp4 = $args{file_mp4};  my $file_mpc = $args{file_mpc};
10985   my $file_ogg = $args{file_ogg}; my $file_ape = $args{file_ape};  my $file_flac= $args{file_flac};
10986   my $file_cda = $args{file_cda}; my $file_wav = $args{file_wav};
10987
10988   $pbar->set_fraction(0);
10989   $pbar_n->set_fraction(0);
10990   $pbar_encode->set_fraction(0);
10991
10992   if   ( $extension_input eq 'wav' ){ $pbar->set_text(" "); }
10993   else { $pbar->set_text(reduce_length_size($file_wav));    }
10994
10995
10996   $pbar_n->set_text(reduce_length_size($file_wav));
10997   draw_input_output($da_arrow_enc,undef,("wav",$extension_output));
10998
10999   if ($button_output_mp3->get_active) {
11000       $frame21->set_label("lame ".$langs{msg077});
11001       ComboBox_set_popdown_strings($ComboBox_encode_type,@encode_type_mp3);
11002       ComboBox_select_this_text(\$ComboBox_encode_type,\@encode_type_mp3,\$encode_type{$extension_output}); # recover the last encode_type value
11003       $pbar_encode->set_text(reduce_length_size($file_mp3));
11004   }
11005   if ($button_output_mp4->get_active) {
11006       $frame21->set_label("faac ".$langs{msg077});
11007       ComboBox_set_popdown_strings($ComboBox_encode_type,@encode_type_mp4);
11008       ComboBox_select_this_text(\$ComboBox_encode_type,\@encode_type_mp4,\$encode_type{$extension_output});
11009       $pbar_encode->set_text(reduce_length_size($file_mp4));
11010   }
11011   if ($button_output_mpc->get_active) {
11012       $frame21->set_label("mppenc ".$langs{msg077});
11013       ComboBox_set_popdown_strings($ComboBox_encode_type,@encode_type_mpc);
11014       ComboBox_select_this_text(\$ComboBox_encode_type,\@encode_type_mpc,\$encode_type{$extension_output});
11015       $pbar_encode->set_text(reduce_length_size($file_mpc));
11016   }
11017   if ($button_output_ogg->get_active) {
11018       $frame21->set_label("oggenc ".$langs{msg077});
11019       ComboBox_set_popdown_strings($ComboBox_encode_type,@encode_type_ogg);
11020       ComboBox_select_this_text(\$ComboBox_encode_type,\@encode_type_ogg,\$encode_type{$extension_output});
11021       $pbar_encode->set_text(reduce_length_size($file_ogg));
11022   }
11023   if ($button_output_ape->get_active) {
11024       $frame21->set_label("ape ".$langs{msg077});
11025       $pbar_encode->set_text(reduce_length_size($file_ape));
11026   }
11027   if ($button_output_flac->get_active) {
11028       $frame21->set_label("flac ".$langs{msg077});
11029       $pbar_encode->set_text(reduce_length_size($file_flac));
11030   }
11031   if ($button_output_wav->get_active) {
11032       $frame21->set_label("wav ".$langs{msg077});
11033       $pbar_encode->set_text(" ");
11034   }
11035
11036   $status_bar->push($context_id, " ");
11037}
11038
11039#####------------------------------------------#####
11040# calcule the space to be reduced to obtain a standard string'size
11041# we note that the char numbers are widthest than letters for Sans font type.
11042# substr string, pos, [n, replacement]
11043# Extracts and returns a substring n characters long, starting at character
11044# position pos, from a given string.
11045sub reduce_length_size{
11046    my $string = shift;
11047    my $max_length = shift || 160;
11048    my $length = length( $string );
11049    my $sub = $length - $max_length;
11050    #print("\n add = $add\n");
11051    if ($sub < 0 ){$sub = 0 ;}
11052    $string = substr($string,  $sub); # cut, remove $sub bytes
11053    #print("\n  string = $string\n");
11054    return $string;
11055}
11056
11057
11058#####-------------vetor-com-arquivos--------------#####
11059#####----- array of mp3, mp4, mpc, ... files -----#####
11060
11061sub eval_to_unicode {
11062   my $filepath_local   = shift;
11063   my $filepath_unicode = undef;
11064
11065   #my $filepath = filename_from_unicode $filepath if( ! -e $filepath );
11066
11067   eval { $filepath_unicode = filename_to_unicode $filepath_local;   };    #  All input data MUST be convertted to unicode reversible
11068   if ($@) { #$filepath_unicode = filename_display_name $filepath_local;   # 'filename_display_name' convert to unicode not reversible!
11069             #$filepath_unicode = $filepath_local;                         #  change nothing!
11070             warn "\nTry to read the file <$filepath_local>, maybe some Wide character: $!";
11071           }
11072   return $filepath_unicode;
11073}
11074
11075sub eval_from_unicode { # not used
11076   my %args = ( row      => undef,
11077                filepath => undef,
11078                @_,      # argument pair list goes here
11079	      );
11080   my $row = $args{row}; my $filepath = $args{filepath};
11081
11082   my $filepath_local = filename_from_unicode $filepath;                                   # convert filename_from_unicode to local encoding
11083   if( ! -e $filepath_local ) { $filepath_local = $files_info[ $row ]{filepath_local}; }   # get original filename encoding
11084
11085   return $filepath_local;
11086}
11087
11088# Find recursively all files. See <man perlfunc> and Recipe 9.7 from Perl Cookbook, 2nd Edition.
11089sub make_array_of_files {
11090    my $directories = shift; # $directories could be an array @directories
11091    # no warnings;
11092    my @all_filepath = ();
11093
11094    #print "\n make_array_of_files --> \$directories = $directories\n";
11095
11096    # use File::Find; # see <man File::Find>
11097    # $File::Find::dir is the current directory name,
11098    # $_ is the current filename within that directory
11099    # $File::Find::name is the complete pathname to the file.
11100    # $File::Find::prune = 1; # prune to true to tell find not to descending into the directory.
11101
11102    find {
11103       #no_chdir => 1, # stops find from descending into directory during processing
11104       #bydepth  => 1, # to visit all files beneath a directoty before the directory itself
11105       #no_chdir => $check_button_recursively->get_active ? $false : $true,
11106
11107       wanted => sub {
11108	  my $filepath = $File::Find::name;
11109
11110	  if ( $File::Find::dir ne $directories and not $check_button_recursively->get_active ){ return; } # not find recursively
11111
11112          if ( not -r $filepath ){ return; }  # -r : File is readable by effective uid/gid.
11113	  if ( -d $filepath ){ return; }      # -d : File is a directory
11114
11115	  push @all_filepath, eval_to_unicode($filepath);
11116	  #push @all_filepath, $filepath;
11117       }
11118    } =>  filename_from_unicode $directories; # convert $directories from unicode to local encoding
11119
11120    # Directory Selection with some Filter
11121    my $filter;
11122    eval { $filter = $file_dialog->get_filter->get_name; };
11123    if (defined $filter){
11124       $filter = undef if ( $filter !~ /^\.(mp3|mp4|mpc|ogg|ape|flac|wav)$/i ); # 'All files' have undef filter
11125    }
11126
11127    @array_of_files = (); # empty , reset the array of files
11128    for (my $i = 0; $i <= $#all_filepath; $i++){
11129       #print_local ("filename[$i] = $all_filepath[$i] \n");
11130       #print "filter = $filter ; filename[$i] = $all_filepath[$i] \n";
11131       my $file = $all_filepath[$i];
11132       next unless defined $file;
11133
11134       if (defined $filter){
11135	  if    ( $filter eq '.mp4' and $file =~ /\.(mp4|m4a|aac)$/i  ){ push @array_of_files, $file; }
11136	  elsif ( $filter eq '.mpc' and $file =~ /\.(mpc|mpp|mp\+)$/i ){ push @array_of_files, $file; }
11137	  elsif ( $file =~ /$filter$/i ){ push @array_of_files, $file; }
11138       }
11139       #/i option: case-insensitive search  ;;  get all supported files
11140       elsif ( $file =~ /\.mp3$/i or $file =~ /\.ogg$/i or $file =~ /\.ape$/i or $file =~ /\.flac$/i or
11141               $file =~ /\.wav$/i or $file =~ /\.(mp4|m4a|aac)$/i or $file =~ /\.(mpc|mpp|mp\+)$/i ){
11142          push @array_of_files, $file;
11143       }
11144    }
11145
11146    @array_of_files = sort_full_path_names(@array_of_files);
11147
11148    if ( @array_of_files <= 0 ){ # There is no supported file!
11149       insert_msg( "$langs{msg090}\n", "small-red");
11150       $status_bar->push($context_id, $langs{msg090} );
11151       return $false;
11152    }
11153
11154    if ( $check_button_recursively->get_active ){
11155           insert_msg( "$langs{msg001}\n" , "small-black");}
11156    else { insert_msg( "$langs{msg002}\n" , "small-black");}
11157
11158    for (my $i=1;$i<=@array_of_files;$i++){  # @array_of_files is the size of array @array_of_files
11159       $i = sprintf ("%02d", $i);
11160       # print on debug textview
11161       insert_msg( "($i): $array_of_files[$i-1] \n", "small")  if ($i % 2 == 1); # alternating colors
11162       insert_msg( "($i): $array_of_files[$i-1] \n", "purple") if ($i % 2 == 0);
11163    }
11164    insert_msg("-----*****-----\n" , "small-black");
11165
11166    return @array_of_files;
11167}
11168
11169###----------- add files to treeview --------------###
11170###------------------- start ----------------------###
11171
11172# The selected file is always the first array element if supported
11173sub put_selected_file_to_first_position {  # not used!
11174   my $file = shift; # selected_file
11175
11176   unless ( $file =~ /\.mp3$/i or $file =~ /\.ogg$/i or $file =~ /\.ape$/i or $file =~ /\.flac$/i or
11177        $file =~ /\.wav$/i or $file =~ /\.(mp4|m4a|aac)$/i or $file =~ /\.(mpc|mpp|mp\+)$/i ){
11178      return;
11179   }
11180   # unshift ARRAY,LIST   : "Prepends list to the front of the array, and returns the new number of elements in the array".
11181   unshift @array_of_files, $file;
11182}
11183
11184
11185sub fill_treeview_files_to_be_played {
11186
11187   #my $model = $treeview_play->get_model; #get the old model from Gtk2::TreeView
11188   #$model->clear;  # clear the old model - see <man Gtk2::ListStore>
11189
11190   my $model = create_model_files_to_be_played();  # add new files to tree model
11191
11192   #print "\nfill_treeview_files_to_be_played --> 1 \n";
11193
11194   $treeview_play->set_model($model);              # add the new model
11195   count_artists_and_album_already_played(update_model_artist => $false, update_model_album => $false);
11196   add_columns( show_play => $true);               # add columns to the treeview_play
11197   make_artist_and_album_treeview_column( make_artist_column => $true, make_album_column => $true, array_with_tracks => \@files_info );
11198
11199   $button_unselec->set_sensitive($true);
11200   $button_selec->set_sensitive($true);
11201   $da->set_sensitive($true);
11202   @rows_already_played = ();
11203
11204   #get_selection_tree(); # select the first file and get info from file
11205
11206   return unless $playing_music;
11207   color_the_selected_tracks_and_scroll( select_color => 'playing' );
11208}
11209
11210fill_treeview_files_to_be_played();
11211
11212###------------------- final ----------------------###
11213###----------- add files to treeview --------------###
11214
11215sub sort_full_path_names{
11216    my @array = @_;
11217    # For example:
11218    # $array[0] = '/tmp/teste/teste4/mus.mp3'
11219    # $array[1] = '/tmp/teste/teste2/teste4/mus.mp3'
11220    # $array[2] = '/tmp/mus.mp3'
11221    # First, sort this array in ascending order by number of '/' character.
11222    # Then, sort in case-insensitively mode.
11223
11224    # sort case-insensitively : @articles = sort {uc($a) cmp uc($b)} @files; (see man perlfunc)
11225    # multiple comparisons in the routine and separate them with ||.
11226    # See Perl Cookbook, 2nd Edition. Chap. 4.16; 10.18 for help;
11227    #my $teste = '/tmp/teste/teste2/teste4/mus.mp3'; $teste =~ s/[^\/]//g; print "teste = $teste\n"; # output:  teste = /////
11228    # first compare the number of directory character '/'.
11229
11230    sub compare_dir {
11231       (my $temp1 = $a) =~ s/[^\/]//g; # remove all character but '/'
11232       (my $temp2 = $b) =~ s/[^\/]//g;
11233       #if ( $temp1 eq $temp2 ){return $false;} # print "temp1 = $temp1 ;; temp2 = $temp2\n";
11234       $temp1 cmp $temp2 # sort them in ascending order by number of '/' character.
11235    }
11236    @array = sort { compare_dir() || uc($a) cmp uc($b) } @array;
11237    return @array;
11238}
11239
11240#####----------- get and show mp3/mp4/ogg ... info --------#####
11241
11242sub get_info_from_file {
11243   my %args = ( show_tag => $true,
11244                @_,     # argument pair list goes here
11245	      );
11246   my $extension_input = $args{extension_input};
11247   my $filepath   = $args{filepath};
11248   my $row = $args{row};
11249   return if ($extension_input eq 'other' or $extension_input eq 'cda');
11250
11251   #print "get_info_from_file --> \$row = $row ;; \$extension_input = $extension_input ;; \$filepath = $filepath\n";
11252
11253   #my $fh = encode("utf8",$file_mp3);
11254   #print ("fh = $fh\n");
11255   #my $fh2 = decode("utf8",$fh);
11256   #print ("fh2 = $fh2\n");
11257
11258   %metadata = (); # clear all tags and technical informations (metadata)
11259
11260   # set init value "" for all 8 tags
11261   $metadata{title}   = "";
11262   $metadata{artist}  = "";
11263   $metadata{album}   = "";
11264   $metadata{comment} = "";
11265   $metadata{genre}   = "";
11266   $metadata{track}       = "";
11267   $metadata{total_track} = "";
11268   $metadata{year}        = "";
11269
11270   # set init value for all 7 technical informations:
11271   $metadata{technical_info}  = "MPEG";
11272   $metadata{bitrate}         = 160;
11273   $metadata{bitrate_nominal} = 160 if ( $extension_input eq 'ogg' );
11274   $metadata{bitrate_average} = $true;
11275   $metadata{frequency}       = 0;
11276   $metadata{mode}            = "";
11277   $metadata{length}          = ""  if ( $extension_input ne 'cda' );
11278   $metadata{fileSize}        = 0;
11279
11280   #------------------------mp3--------------------------#
11281   # Only show the mp3 info for a file whose extension_input is .mp3
11282   if ( $files_info[$row]{extension_input} eq 'mp3' ){
11283      #read_ID3v1_mp3_tag($file_mp3);
11284      #$bitrate_average = read_Xing_mp3_tag($file_mp3);
11285      read_mp3_info_tag(%args);
11286   }
11287   #------------------------mp4--------------------------#
11288   # Only show the mp4 info for a file whose extension_input is .mp4
11289   elsif ( $files_info[$row]{extension_input} eq 'mp4' ){
11290      get_mp4_info_from_file(%args);
11291   }
11292   #------------------------mpc--------------------------#
11293   # Only show the mpc info for a file whose extension_input is .mpc
11294   elsif ( $files_info[$row]{extension_input} eq 'mpc' ){
11295      show_APE_tag(%args);
11296      show_mpc_info(%args);
11297   }
11298   #------------------------ogg--------------------------#
11299   # Only show the ogg info for a file whose extension_input is .ogg
11300   elsif ( $files_info[$row]{extension_input} eq 'ogg' ){
11301      show_ogg_tag(%args);
11302      show_ogg_info(%args);
11303      #get_ogg_info_from_file();
11304   }
11305   #------------------------ape--------------------------#
11306   # Only show the ape info for a file whose extension_input is .ape
11307   elsif ( $files_info[$row]{extension_input} eq 'ape' ){
11308      show_APE_tag(%args);
11309      show_ape_info(%args);
11310   }
11311   #------------------------flac-------------------------#
11312   # Only show the flac info for a file whose extension_input is .flac
11313   elsif ( $files_info[$row]{extension_input} eq 'flac' ){
11314      show_flac_info(%args);
11315   }
11316   #------------------------wav--------------------------#
11317   elsif ( $files_info[$row]{extension_input} eq 'wav' ){
11318      show_APE_tag(%args);
11319      show_wav_info(%args);
11320   }
11321
11322   #---------- update the all info for one $row ---------#
11323
11324   # get all technical informations:
11325   $files_info[$row]{technical_info}  = $metadata{technical_info};
11326   $files_info[$row]{bitrate}         = $metadata{bitrate};  # more precise value
11327   $files_info[$row]{bitrate_nominal} = $metadata{bitrate_nominal} if ( $extension_input eq 'ogg' );
11328   $files_info[$row]{bitrate_average} = $metadata{bitrate_average};
11329   $files_info[$row]{frequency}       = $metadata{frequency};
11330   $files_info[$row]{mode}            = $metadata{mode};
11331   $files_info[$row]{length}          = $metadata{length}         if ( $extension_input ne 'cda' );
11332   #$files_info[$row]{cda_total_time}  = $metadata{cda_total_time} if ( $extension_input eq 'cda' );
11333   $files_info[$row]{fileSize}        = $metadata{fileSize};
11334
11335   # get the 8 tags
11336   $files_info[$row]{title}   = $metadata{title};
11337   $files_info[$row]{artist}  = $metadata{artist};
11338   $files_info[$row]{album}   = $metadata{album};
11339   $files_info[$row]{comment} = $metadata{comment};
11340   $files_info[$row]{year}    = $metadata{year};
11341   $files_info[$row]{genre}   = $metadata{genre};
11342   $files_info[$row]{track}       = $metadata{track}; # track = track_number
11343   $files_info[$row]{total_track} = $metadata{total_track};
11344
11345   #print "\n get_info_from_file --> title = $files_info[$row]{title} ;; bitrate = $metadata{bitrate} ;; bitrate_average = $metadata{bitrate_average} ;; \$row = $row\n";
11346
11347   set_tag_info($row) if $args{show_tag};
11348
11349   # remove the decimals numbers: 146.5  --> 146
11350   $files_info[$row]{bitrate} = sprintf("%.0f", number_value( $metadata{bitrate} ) ); #Kbitrate
11351}
11352
11353sub set_tag_info {
11354   my $row = shift;
11355   return unless defined $row;
11356
11357   #print " set_tag_info -->1 row = $row ;; title = $files_info[$row]{title} ;; track = $files_info[$row]{track}\n";
11358
11359   # Set all 7 technical informations on the tab entries:
11360   $frame31->set_label ( uc($files_info[$row]{extension_input}) . " Info" );
11361   $label_mpeg->set_label ( $files_info[$row]{technical_info} );
11362   $label_freq->set_label ( $files_info[$row]{frequency} ? "$langs{name041}: $files_info[$row]{frequency} Hz" : "$langs{name041}:" );
11363   $label_mode->set_label ( ($files_info[$row]{extension_input} eq 'mp3' ? $langs{name031} : $langs{name043}) . ": $files_info[$row]{mode}" );
11364   $label_time->set_label ( $files_info[$row]{extension_input} ne 'cda' ? "$langs{name042}: $files_info[$row]{length}" : "$langs{name042}: $files_info[$row]{length} ($files_info[$row]{cda_total_time})" );
11365   $label_size->set_label ( $files_info[$row]{fileSize} ? "$langs{name048}: $files_info[$row]{fileSize} MB" : "$langs{name048}:" );
11366
11367   if ( $files_info[$row]{extension_input} eq 'mp3' and $files_info[$row]{bitrate} ){
11368      $label_kbps->set_label ( ($files_info[$row]{bitrate_average} ? 'Variable' : 'Constant') . "\nBitrate: $files_info[$row]{bitrate} Kb/s");
11369   }
11370   elsif ( $files_info[$row]{extension_input} eq 'ogg' ){
11371      $label_kbps->set_label ("Bitrate: $files_info[$row]{bitrate} Kb/s\nNominal : $files_info[$row]{bitrate_nominal} Kb/s");
11372   }
11373   elsif ( $files_info[$row]{extension_input} eq 'cda' ){
11374      $label_kbps->set_label ( $files_info[$row]{bitrate} ? "Bitrate: 1411.2 Kb/s" : "Bitrate:" );
11375   }
11376   else {
11377      $label_kbps->set_label ( $files_info[$row]{bitrate} ? "Bitrate: $files_info[$row]{bitrate} Kb/s" : "Bitrate:" );
11378   }
11379
11380   # To not execute the 'sub change_tag'
11381   $abandon_change_tag = $true; # if '$abandon_change_tag eq $true' then force to abandon the 'sub change_tag'
11382
11383   # Set all 8 tags  on the tab entries:
11384   $entry_title->set_text( $files_info[$row]{title} )                 if defined $files_info[$row]{title};
11385   #$entry_title->set_text( filename_from_unicode $files_info[$row]{title} )                 if defined $files_info[$row]{title};
11386   $entry_artist->set_text( $files_info[$row]{artist} )               if defined $files_info[$row]{artist};
11387   $entry_album->set_text( $files_info[$row]{album} )                 if defined $files_info[$row]{album};
11388   $entry_year->set_text( $files_info[$row]{year} )                   if defined $files_info[$row]{year};
11389   $entry_tn->set_text( $files_info[$row]{track} )                    if defined $files_info[$row]{track};
11390   $entry_tt->set_text( $files_info[$row]{total_track} )              if defined $files_info[$row]{total_track};
11391   $entry_comment->set_text( $files_info[$row]{comment} )             if defined $files_info[$row]{comment};
11392   $ComboBoxEntry_genre->child->set_text( $files_info[$row]{genre} )  if defined $files_info[$row]{genre};
11393
11394   $abandon_change_tag = $false; # if '$abandon_change_tag eq $true' then force to abandon the 'sub change_tag'
11395
11396   #print " set_tag_info -->2 row = $row ;; title = $files_info[$row]{title} ;; track = $files_info[$row]{track}\n";
11397   $button_save_tag->set_sensitive($false);
11398}
11399
11400#  Perl in a Nutshell - OReilly - By Ellen Siever, Stephen Spainhour & Nathan Patwardhan
11401#  See too: <man perlfunc>
11402
11403	# read (filehandle, $var, length [,offset]);
11404	# "Attempts to read length bytes of data into variable $var from the specified filehandle.
11405	# The offset, if specified, says where in the variable $var to start putting bytes,
11406	# so that you can do a read into the middle of a string."
11407
11408	# unpack ("template", string);
11409        # "Takes a string representing a data structure and expands it into a list value,
11410	# returning the list value. (unpack does the reverse of pack.) In a scalar context,
11411	# it can be used to unpack a single value. The template ... specifies the order and
11412	# type of the values to be unpacked. "
11413
11414	# pack ("template", list);
11415        # "Takes a list of values and packs it into a binary structure, returning the string
11416	# containing the structure. The template is a sequence of characters that give the
11417	# order and type of values, as follows:
11418	# C : An unsigned char value
11419	# x : A null byte
11420	# Each character may optionally be followed by a number that gives a repeat count.
11421	# Field specifiers may be separated by whitespace, which will be ignored."
11422
11423	# This i a example of content of mp3 file:
11424        # ID3
11425	#
11426	#
11427	#
11428
11429	# GripTYER
11430	#
11431
11432
11433sub show_mpc_info {
11434   my %args = ( @_ );
11435   my $extension_input = $args{extension_input}; my $filepath = $args{filepath}; my $row = $args{row};
11436   return unless $extension_input eq 'mpc';
11437
11438    my $header;
11439    my $Streamversion;
11440    my $buffer;
11441
11442    use constant MPCHEADERFLAG => 'MP+';
11443
11444    my @profileNames = (
11445		'na', "Unstable/Experimental", 'na', 'na',
11446		'na', "below Telephone", "below Telephone", "Telephone",
11447		"Thumb", "Radio", "Standard", "Xtreme",
11448		"Insane", "BrainDead", "above BrainDead", "above BrainDead"
11449    );
11450
11451    my @samplFreq = qw(44100 48000 37800 32000);
11452
11453    open(IN, filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
11454
11455    binmode IN;
11456    $fileSize = -s filename_from_unicode $filepath;
11457
11458    # Read musepack header.
11459    read (IN, $header, 3); # 3 byte for 'MP+'
11460
11461    if ($header ne MPCHEADERFLAG) { return $false; }
11462
11463    # read one byte
11464    read IN, $Streamversion, 1;
11465    $Streamversion = unpack "C", $Streamversion;
11466    #print "Streamversion = $Streamversion\n";
11467    if ( $Streamversion < 7 ){ return; } # Musepack SV version supported
11468
11469    # read 4 byte
11470    read IN, $buffer, 4;
11471    my $frame_count = unpack "L", $buffer;
11472    # print "frame_count = $frame_count\n";
11473
11474    read IN, $buffer, 4; # for Stream Flags ;; 4 bytes = 4 * 8 bits = 32 bits
11475
11476    my $StreamFlags = reverse unpack "b32", $buffer;  #  'b' : A bit string (ascending bit order inside each byte)
11477    #print "StreamFlags = $StreamFlags\n";
11478
11479    my $profile = bin2dec(substr($StreamFlags, 8, 4)); # get the profile number
11480    #print "profile = $profile ;; bin = ",substr($StreamFlags, 8, 4),"\n";
11481    $Frequency = $samplFreq[bin2dec(substr($StreamFlags, 14, 2))];
11482
11483    read IN, $buffer, 12;
11484
11485    $buffer = unpack "L", substr($buffer,8);  # remove 8 byte, remain 4 byte
11486    $buffer = sprintf( "%032b", $buffer ); # convert $StreamFlags to binary base with 32 digits
11487
11488    my $lastValidSamples = bin2dec(substr($buffer, 1, 11));
11489    #print "profile = $profile ;; samplFreq = $Frequency ;; lastValidSamples = $lastValidSamples \n";
11490
11491    my $totalSamples = ($frame_count - 1)*32*36 + $lastValidSamples;
11492    my $totalSeconds = $totalSamples/$Frequency;
11493
11494    my $bitrate = 8 * $fileSize / $totalSeconds; # 8*$filesize = size in bit
11495
11496    $totalSeconds = sprintf("%.0f",$totalSeconds);
11497    # convert x seconds to hour:min:sec format
11498    my ($hour,$min,$sec) = sec_to_time($totalSeconds);    # return ($hour,$min,$sec);
11499
11500    #print "bitrate = $bitrate ;; time = $hour:$min:$sec ;; totalSeconds = $totalSeconds\n";
11501
11502    close(IN);
11503
11504    # if $bitrate > 500 kp/s
11505    if ($bitrate> 500000){$profile = 15;}  # correct the bug of mppenc whem q = 10.0
11506    $profile = $profileNames[$profile];
11507
11508    $Streamversion = sprintf("%0.1f",$Streamversion);
11509    $Technical_Info = "Stream: v$Streamversion\nProfile: '$profile'";
11510    $Time_Min_Sec = $hour > 0 ? "$hour:$min:$sec" : "$min:$sec" ;
11511    $bitrate_original = sprintf("%0.1f",$bitrate/1000);
11512    $fileSize = sprintf("%.1f", $fileSize/(1024 * 1024) ); # MBytes
11513    $Frequency = sprintf("%.0f",$Frequency);
11514
11515
11516   $encode = 'average';
11517   $bitrate_average = $true;
11518   $mode_channel = 2;
11519
11520   # get all technical informations:
11521   $metadata{technical_info}  = $Technical_Info;
11522   $metadata{bitrate}         = $bitrate_original;
11523   $metadata{bitrate_nominal} = 160;               # only for ogg
11524   $metadata{bitrate_average} = $bitrate_average;  # $true or $false
11525   $metadata{frequency}       = $Frequency;
11526   $metadata{mode}            = $mode_channel;
11527   $metadata{length}          = $Time_Min_Sec if ( $extension_input ne 'cda' );
11528   $metadata{fileSize}        = $fileSize;
11529
11530
11531    # SV 7.0 Header - Item    Size ( 1 byte = 8 bit )
11532    # Preample                  3 bytes 'MP+'
11533
11534    # Streamversion             1 byte
11535
11536    #                                   StreamMinorVersion 4 bits
11537    #                                   StreamMajorVersion 4 bits
11538    #                                               sum =  8 bits = 1 byte
11539    # FrameCount                4 bytes
11540
11541    # Stream Flags              4 bytes
11542
11543    #                                   IntensityStereo  1 bit
11544    #                                   MidSideStereo    1 bit
11545    #                                   MaxBand          6 bits
11546    #                                   Profile          4 bits
11547    #                                   Link             2 bits
11548    #                                   SampleFreq       2 bits
11549    #                                   MaxLevel        16 bits
11550    #                                             sum = 32 bits = 4 bytes
11551    # TitleGain                 2 bytes
11552    # TitlePeak                 2 bytes
11553    # AlbumGain                 2 bytes
11554    # AlbumPeak                 2 bytes
11555    #
11556    # TrueGapless               1 bit
11557    # LastFrameLength          11 bits
11558    # ...
11559
11560    # tell    Returns the current position in bytes for FILEHANDLE,
11561    # sysread FILEHANDLE,SCALAR,LENGTH,OFFSET  ;; Returns the number of bytes actually read
11562    # syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET
11563    # substr EXPR,OFFSET,LENGTH,REPLACEMENT  ;; Extracts a substring out of EXPR and returns it.
11564}
11565
11566sub show_ape_info {
11567   my %args = ( @_ );
11568   my $extension_input = $args{extension_input}; my $filepath = $args{filepath}; my $row = $args{row};
11569   return unless $extension_input eq 'ape';
11570
11571    my $header;
11572    my $buffer;
11573
11574    my $CompressionLevel; my $Channels; my $HeaderBytes;
11575    my $TerminatingBytes; my $TotalFrames; my $FinalFrameBlocks;
11576    my $BlocksPerFrame; my $BitsPerSample;
11577
11578    use constant APEHEADERFLAG => 'MAC ';
11579
11580    use constant COMPRESSION_LEVEL_FAST  =>          '1000';
11581    use constant COMPRESSION_LEVEL_NORMAL =>         '2000';
11582    use constant COMPRESSION_LEVEL_HIGH   =>         '3000';
11583    use constant COMPRESSION_LEVEL_EXTRA_HIGH =>     '4000';
11584    use constant COMPRESSION_LEVEL_INSANE_HIGH   =>  '5000';
11585    use constant COMPRESSION_LEVEL_BRAINDEAD_HIGH => '6000';
11586
11587    my @CompLevel = ( 'n', 'Fast', 'Normal', 'High', 'Extra High',
11588                      'Insane', 'BrainDead' );
11589
11590    open(IN, filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
11591
11592    binmode IN;
11593    $fileSize = -s filename_from_unicode $filepath;
11594    #print "fileSize = $fileSize bytes\n";
11595
11596    # Read ape header.
11597    read (IN, $header, 4); # 4 byte for 'MAC '
11598
11599    if ($header ne APEHEADERFLAG) { return $false; }
11600
11601    read IN, my $version, 2;  # 2 byte for version
11602    $version = unpack "S", $version;
11603    $version = sprintf( "%.2f", $version/1000 );
11604    $version = number_value($version);
11605    #print "version = $version\n";
11606
11607    if ( $version < 3.98 ) { # old header format for APE
11608
11609       read IN, $CompressionLevel, 2;  # 2 byte for CompressionLevel
11610       $CompressionLevel = unpack "S", $CompressionLevel;
11611       #print "CompressionLevel = $CompressionLevel\n";
11612
11613       read IN, $Channels, 4;  # 2 byte = 16 bits, for Channels
11614       $Channels = unpack "S", substr($Channels, 2, 2);  # don't read flags not used
11615       #print "Channels = $Channels\n";
11616
11617       read IN, $Frequency, 4;  # 4 byte for SampleRate
11618       $Frequency = unpack "L", $Frequency;
11619       #print "Samples per second(Frequency):= $Frequency\n";
11620
11621       read IN, $HeaderBytes, 4;  # 4 byte for HeaderBytes
11622       $HeaderBytes = unpack "L", $HeaderBytes;
11623       #print "HeaderBytes = $HeaderBytes\n";
11624
11625       read IN, $TerminatingBytes, 4;  # 4 byte for TerminatingBytes
11626       $TerminatingBytes = unpack "L", $TerminatingBytes;
11627       #print "TerminatingBytes = $TerminatingBytes\n";
11628
11629       read IN, $TotalFrames, 4;  # 4 byte for TotalFrames
11630       $TotalFrames = unpack "L", $TotalFrames;
11631       #print "TotalFrames = $TotalFrames\n";
11632
11633       read IN, $FinalFrameBlocks, 4;  # 4 byte for FinalFrameBlocks
11634       $FinalFrameBlocks = unpack "L", $FinalFrameBlocks;
11635       #print "FinalFrameBlocks = $FinalFrameBlocks\n";
11636
11637    }
11638
11639    $BlocksPerFrame = (($version >= 3.90) || (($version >= 3.80) && ($CompressionLevel == COMPRESSION_LEVEL_EXTRA_HIGH))) ? 0x12000 : 0x02400;
11640    if ($version >= 3.95){$BlocksPerFrame = 0x48000; } # 0x48000 (hex) = 294912 (decimal)
11641
11642    if ( $version >= 3.98 ) {  # current header format for APE files
11643
11644       # 8 bytes = 4 bytes for 'MAC ' + 4 bytes for version
11645       #$APE_DESCRIPTOR, 44; # 4*7 + 16 (MD5) = 44 bytes
11646       #  total = 52 bytes = 8 + 44
11647       seek(IN, 8 + 4, 0);
11648
11649       read IN, $HeaderBytes, 4;  # 4 byte for HeaderBytes
11650       $HeaderBytes = unpack "L", $HeaderBytes;
11651       #print "HeaderBytes = $HeaderBytes\n";
11652
11653       #  total = 52 bytes = 8 + 44
11654       seek(IN, 52, 0);
11655
11656       read IN, $CompressionLevel, 2;  # 2 byte for CompressionLevel
11657       $CompressionLevel = unpack "S", $CompressionLevel;
11658       #print "CompressionLevel = $CompressionLevel\n";
11659
11660       read IN, my $FormatFlags, 2;  # 2 byte for FormatFlags (not used)
11661
11662       read IN, $BlocksPerFrame, 4;  # 4 byte for BlocksPerFrame
11663       $BlocksPerFrame = unpack "L", $BlocksPerFrame;
11664       #print "BlocksPerFrame = $BlocksPerFrame\n";
11665
11666       read IN, $FinalFrameBlocks, 4;
11667       $FinalFrameBlocks = unpack "L", $FinalFrameBlocks;
11668       #print "FinalFrameBlocks = $FinalFrameBlocks\n";
11669
11670       read IN, $TotalFrames, 4;
11671       $TotalFrames = unpack "L", $TotalFrames;
11672       #print "TotalFrames = $TotalFrames\n";
11673
11674       read IN, $BitsPerSample, 2;  # 2 byte for BitsPerSample
11675       $BitsPerSample = unpack "S", $BitsPerSample;
11676       #print "BitsPerSample = $BitsPerSample\n";
11677
11678       read IN, $Channels, 2;  # 2 byte for Channels
11679       $Channels = unpack "S", $Channels;
11680       #print "Channels = $Channels\n";
11681
11682       read IN, $Frequency, 4;  # 4 byte for SampleRate
11683       $Frequency = unpack "L", $Frequency;
11684       #print "Samples per second(Frequency):= $Frequency\n";
11685
11686    }
11687
11688    close(IN);
11689
11690    # from src/MACLib/APEHeader.cpp  ;; Total Samples = $TotalBlocks
11691    my $TotalBlocks = ($TotalFrames == 0) ? 0 : ($TotalFrames - 1) * $BlocksPerFrame + $FinalFrameBlocks ;
11692    my $totalSeconds = $TotalBlocks/$Frequency;
11693
11694    my $bitrate = ($totalSeconds <= 0) ? 0 :  ($fileSize - $HeaderBytes) * 8 / $totalSeconds;  # 8*$filesize = size in bit
11695    $bitrate = sprintf("%.1f",$bitrate/1000);
11696
11697    $totalSeconds = sprintf("%.0f",$totalSeconds);
11698    # convert x seconds to hour:min:sec format
11699    my ($hour,$min,$sec) = sec_to_time($totalSeconds);    # return ($hour,$min,$sec);
11700
11701    #print "Kbitrate = $bitrate ;; time = $hour:$min:$sec ;; totalSeconds = $totalSeconds\n";
11702
11703    $Frequency = sprintf("%.0f", $Frequency );
11704    #my $Compression_ratio = sprintf("%.0f", ($bitrate/1411.2) * 100 );  # approximate value
11705    $Technical_Info = "MAC version: $version\nComp: " . $CompLevel[$CompressionLevel/1000] ; # . "\nCompression:~ $Compression_ratio%";
11706    $mode_channel = $Channels;
11707    $Time_Min_Sec = $hour > 0 ? "$hour:$min:$sec" : "$min:$sec" ;
11708    $bitrate_original = $bitrate;
11709    $fileSize = sprintf("%.1f", $fileSize/(1024 * 1024) ); # MBytes
11710
11711
11712   $encode = 'average';
11713   $bitrate_average = $true;
11714
11715   # get all technical informations:
11716   $metadata{technical_info}  = $Technical_Info;
11717   $metadata{bitrate}         = $bitrate_original;
11718   $metadata{bitrate_nominal} = 160;               # only for ogg
11719   $metadata{bitrate_average} = $bitrate_average;  # $true or $false
11720   $metadata{frequency}       = $Frequency;
11721   $metadata{mode}            = $mode_channel;
11722   $metadata{length}          = $Time_Min_Sec if ( $extension_input ne 'cda' );
11723   $metadata{fileSize}        = $fileSize;
11724
11725    #----------------------------OLD APE files-----------------------------#
11726
11727    # APE header structure for old APE files (version <= 3.97) : APEHeader.h and APEHeader.cpp
11728
11729    # APE Header - Item    Size ( 1 byte = 8 bits ) -    Type     - Total 32 bytes - see <man perlfunc>
11730    # Preample                  4 bytes 'MAC '          char c
11731    # version                   2 bytes    'S' unsigned int16   # version number * 1000 (3.81 = 3810)
11732    # CompressionLevel          2 bytes                 int16
11733    # FormatFlags               2 bytes                 int16   # any format flags (for future use)
11734    # Channels                  2 bytes                 int16   # the number of channels (1 or 2)
11735    # SampleRate                4 bytes    'L' unsigned int32   # the sample rate (typically 44100)
11736    # HeaderBytes               4 bytes                 int32   # the bytes after the MAC header that compose the WAV header
11737    # TerminatingBytes          4 bytes                 int32   # the bytes after that raw data (for extended info)
11738    # TotalFrames               4 bytes                 int32   # the number of frames in the file
11739    # FinalFrameBlocks          4 bytes                 int32   # the number of samples in the final frame
11740
11741
11742    #----------------------------NEW APE files-----------------------------#
11743
11744    # APE header structure for new APE files (version >= 3.98) : from src/MACLib/MACLib.h
11745
11746    #JUNK - any amount of "junk" before the APE_DESCRIPTOR (so people that put ID3v2 tags on the files aren't hosed)
11747    #APE_DESCRIPTOR - defines the sizes (and offsets) of all the pieces, as well as the MD5 checksum
11748    #APE_HEADER - describes all of the necessary information about the APE file
11749
11750    #APE_DESCRIPTOR structure (file header that describes lengths, offsets, etc.)
11751    # char    cID[4];                             // should equal 'MAC '
11752    # uint16  nVersion;                           // version number * 1000 (3.81 = 3810)
11753    # uint32  nDescriptorBytes;                   // the number of descriptor bytes (allows later expansion of this header)
11754    # uint32  nHeaderBytes;                       // the number of header APE_HEADER bytes
11755    # uint32  nSeekTableBytes;                    // the number of bytes of the seek table
11756    # uint32  nHeaderDataBytes;                   // the number of header data bytes (from original file)
11757    # uint32  nAPEFrameDataBytes;                 // the number of bytes of APE frame data
11758    # uint32  nAPEFrameDataBytesHigh;             // the high order number of APE frame data bytes
11759    # uint32  nTerminatingDataBytes;              // the terminating data of the file (not including tag data)
11760    # uint8   cFileMD5[16];                       // the MD5 hash of the file (see notes for usage... it's a littly tricky)
11761
11762    #APE_HEADER structure (describes the format, duration, etc. of the APE file)
11763    # uint16    nCompressionLevel;                 // the compression level (see defines I.E. COMPRESSION_LEVEL_FAST)
11764    # uint16    nFormatFlags;                      // any format flags (for future use)
11765    # uint32    nBlocksPerFrame;                   // the number of audio blocks in one frame
11766    # uint32    nFinalFrameBlocks;                 // the number of audio blocks in the final frame
11767    # uint32    nTotalFrames;                      // the total number of frames
11768    # uint16    nBitsPerSample;                    // the bits per sample (typically 16)
11769    # uint16    nChannels;                         // the number of channels (1 or 2)
11770    # uint32    nSampleRate;                       // the sample rate (typically 44100)
11771
11772
11773    #-------------------------------INFO----------------------------------#
11774
11775    #  fill the APE info structure (see APE_3.96b8_Source.tar.bz2  :  Monkey/Source/MACLib/APEInfo.cpp)
11776    #m_APEFileInfo.nBlocksPerFrame       = ((APEHeader.nVersion >= 3900) || ((APEHeader.nVersion >= 3800) && (APEHeader.nCompressionLevel == COMPRESSION_LEVEL_EXTRA_HIGH))) ? 0x12000 : 0x02400;
11777    #                                        if ((APEHeader.nVersion >= 3950)) m_APEFileInfo.nBlocksPerFrame = 0x48000;
11778    #m_APEFileInfo.nBitsPerSample        = (m_APEFileInfo.nFormatFlags & MAC_FORMAT_FLAG_8_BIT) ? 8 : ((m_APEFileInfo.nFormatFlags & MAC_FORMAT_FLAG_24_BIT) ? 24 : 16);
11779    #m_APEFileInfo.nBytesPerSample       = m_APEFileInfo.nBitsPerSample / 8;
11780    #m_APEFileInfo.nBlockAlign           = m_APEFileInfo.nBytesPerSample * m_APEFileInfo.nChannels;
11781    #m_APEFileInfo.nTotalBlocks          = (APEHeader.nTotalFrames == 0) ? 0 : ((APEHeader.nTotalFrames -  1) * m_APEFileInfo.nBlocksPerFrame) + APEHeader.nFinalFrameBlocks;
11782    #m_APEFileInfo.nWAVHeaderBytes       = (APEHeader.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER) ? sizeof(WAVE_HEADER) : APEHeader.nHeaderBytes;
11783    #m_APEFileInfo.nWAVTerminatingBytes  = int(APEHeader.nTerminatingBytes);
11784    #m_APEFileInfo.nWAVDataBytes         = m_APEFileInfo.nTotalBlocks * m_APEFileInfo.nBlockAlign;
11785    #m_APEFileInfo.nWAVTotalBytes        = m_APEFileInfo.nWAVDataBytes + m_APEFileInfo.nWAVHeaderBytes + m_APEFileInfo.nWAVTerminatingBytes;
11786    #m_APEFileInfo.nLengthMS             = int((double(m_APEFileInfo.nTotalBlocks) * double(1000)) / double(m_APEFileInfo.nSampleRate));
11787    #m_APEFileInfo.nAverageBitrate       = (m_APEFileInfo.nLengthMS <= 0) ? 0 : int((double(m_APEFileInfo.nAPETotalBytes) * double(8)) / double(m_APEFileInfo.nLengthMS));
11788    #m_APEFileInfo.nDecompressedBitrate  = (m_APEFileInfo.nBlockAlign * m_APEFileInfo.nSampleRate * 8) / 1000;
11789}
11790
11791
11792sub show_flac_info {
11793   my %args = ( @_ );
11794   my $extension_input = $args{extension_input}; my $filepath = $args{filepath}; my $row = $args{row};
11795   return unless $extension_input eq 'flac';
11796
11797    my $header;
11798    my $buffer;
11799
11800    use constant FLACHEADERFLAG => 'fLaC'; # FLAC__STREAM_SYNC_STRING
11801
11802    my @block_type = ( 'STREAMINFO', 'PADDING', 'APPLICATION', 'SEEKTABLE',
11803                       'VORBIS_COMMENT', 'CUESHEET', 'reserved' );
11804
11805    open(IN, filename_from_unicode $filepath ) or die "Can't open <$filepath>: $!, stopped";
11806
11807    binmode IN;
11808    $fileSize = -s filename_from_unicode $filepath;
11809
11810    # Read flac header.
11811    read (IN, $header, 4); # 4 byte for 'fLaC'
11812
11813    if ($header ne FLACHEADERFLAG) { return $false; }
11814
11815    # METADATA_BLOCK_HEADER (4 bytes)
11816    # A block header that specifies the type and size of the metadata block data.
11817
11818    #-------------------------------------------------#
11819    my $vorbis_comment;
11820    my $flag = 0;
11821    my $it = 0;  # to avoid infinite loop
11822
11823    while ( $flag == 0 and $it < 9 )  # Search for vorbis_comment
11824    {
11825       # METADATA_BLOCK_HEADER (4 bytes)
11826       # A block header that specifies the type and size of the metadata block data.
11827       read IN, $buffer, 4;  # 4 byte (=32 bits)
11828       $buffer = unpack "B32", $buffer;
11829       $flag = bin2dec(substr($buffer, 0, 1)); # flag: = 1 if last metadata, 0 otherwise
11830       $buffer = substr($buffer, 1); # remove the first 1 bit
11831       #print "flag = $flag\n";
11832
11833       my $b_type = bin2dec(substr($buffer, 0, 7));
11834       $buffer = substr($buffer, 7); # remove the first 7 bits, remain 24 bits
11835       #print "block_type = $block_type[$b_type]\n";
11836
11837       my $block_size = bin2dec($buffer);
11838       #print "block_size = $block_size bytes\n";
11839
11840       # METADATA_BLOCK PADDING or APPLICATION or SEEKTABLE or VORBIS_COMMENT
11841       read IN, $buffer, $block_size;  # read $block_size bytes to buffer
11842
11843       if ( $block_type[$b_type] eq 'VORBIS_COMMENT' ){
11844          $vorbis_comment = $buffer;
11845	  #print "vorbis = $buffer\n";
11846	  read_vorbis_comment_tag($vorbis_comment,$extension_input);
11847       }
11848       $it = $it +1;
11849    }
11850    #-------------------------------------------------#
11851
11852    # 8 bytes = 4 bytes for ''fLaC' + 4 bytes for first block_header
11853    seek(IN, 4 + 4, 0);
11854
11855    # METADATA_BLOCK_STREAMINFO (34 bytes)
11856    # b   A bit string (ascending bit order inside each byte, like vec()).
11857    # B   A bit string (descending bit order inside each byte).  - see <man perlfunc>
11858
11859    read IN, my $min_blocksize, 2;  # 2 bytes = 16 bits for min_blocksize
11860    $min_blocksize = bin2dec(unpack "B16", $min_blocksize);
11861    #print "min_blocksize = $min_blocksize\n";
11862
11863    read IN, my $max_blocksize, 2;  # 2 bytes for max_blocksize
11864    $max_blocksize = bin2dec(unpack "B16", $max_blocksize);
11865    #print "max_blocksize = $max_blocksize\n";
11866
11867    read IN, my $min_framesize, 3;  # 3 bytes = 24 bits for min_framesize
11868    $min_framesize = unpack "B24", $min_framesize;
11869    $min_framesize = bin2dec($min_framesize);
11870    #print "min_framesize = $min_framesize\n";
11871
11872    read IN, my $max_framesize, 3;  # 3 bytes = 24 bits for max_framesize
11873    $max_framesize = unpack "B24", $max_framesize;
11874    $max_framesize = bin2dec($max_framesize);
11875    #print "max_framesize = $max_framesize\n";
11876
11877    read IN, $buffer, 8;  # 8 bytes = (20+3+5+36) bits = 64 bits
11878    $buffer = unpack "B64", $buffer;
11879    $Frequency = bin2dec(substr($buffer, 0, 20)); #SampleRate frequency
11880    $buffer = substr($buffer, 20); # remove the first 20 bits
11881    #print "sample_rate = $Frequency\n";
11882
11883    my $Channels = 1 + bin2dec(substr($buffer, 0, 3));
11884    $buffer = substr($buffer, 3); # remove the first 3 bits
11885    #print "Channels = $Channels\n";
11886
11887    my $bits_per_sample = 1 + bin2dec(substr($buffer, 0, 5));
11888    $buffer = substr($buffer, 5); # remove the first 5 bits
11889    #print "bits_per_sample = $bits_per_sample\n";
11890
11891    my $Total_samples = bin2dec(substr($buffer, 0, 36));
11892    #print "Total_samples = $Total_samples\n";
11893
11894    read IN, $buffer, 16; # 16 bytes for MD5 signature
11895
11896    close(IN);
11897
11898    my $totalSeconds = $Total_samples/$Frequency;
11899    my $bitrate = ($totalSeconds <= 0) ? 0 :  $fileSize * 8 / $totalSeconds;
11900    $bitrate = sprintf("%.1f",$bitrate/1000);
11901    my $Compression_ratio = 100 * $fileSize / ($bits_per_sample / 8 * $Channels * $Total_samples);
11902    $Compression_ratio = sprintf("%.2f",$Compression_ratio);
11903
11904    $totalSeconds = sprintf("%0.0f",$totalSeconds);
11905    # convert x seconds to hour:min:sec format
11906    my ($hour,$min,$sec) = sec_to_time($totalSeconds);    # return ($hour,$min,$sec);
11907
11908    #print "bitrate = $bitrate ;; time = $hour:$min:$sec ;; totalSeconds = $totalSeconds\n";
11909    #print "Compression_ratio = $Compression_ratio\n";
11910
11911    $Frequency = sprintf("%.0f", $Frequency );
11912    $Technical_Info = "Compression: $Compression_ratio%";
11913    $mode_channel = $Channels;
11914    $Time_Min_Sec = $hour > 0 ? "$hour:$min:$sec" : "$min:$sec" ;
11915    $bitrate_original = $bitrate;
11916    $fileSize = sprintf("%.1f", $fileSize/(1024 * 1024) ); # MBytes
11917
11918   $encode = 'average';
11919   $bitrate_average = $true;
11920
11921   # get all technical informations:
11922   $metadata{technical_info}  = $Technical_Info;
11923   $metadata{bitrate}         = $bitrate_original;
11924   $metadata{bitrate_nominal} = 160;               # only for ogg
11925   $metadata{bitrate_average} = $bitrate_average;  # $true or $false
11926   $metadata{frequency}       = $Frequency;
11927   $metadata{mode}            = $mode_channel;
11928   $metadata{length}          = $Time_Min_Sec if ( $extension_input ne 'cda' );
11929   $metadata{fileSize}        = $fileSize;
11930
11931
11932# See flac-1.1.2/doc/html/format.html#metadata_block_streaminfo
11933
11934# FLAC Header -         Size ( 1 byte = 8 bits ) -      Type     - Total 4 bytes
11935    # Preample                  4 bytes 'fLaC          char c
11936
11937# METADATA_BLOCK_HEADER           Size          Type       - Total 4 bytes
11938    # Flags   (1 byte)
11939    #                           1 bit                           #'1' if this block is the last metadata block before the audio blocks, '0' otherwise.
11940    #			      + 7 bits                          # 0 : STREAMINFO ;; 1 : PADDING ;; 2 : APPLICATION ;; 3 : SEEKTABLE ;; 4 : VORBIS_COMMENT
11941    #   		      = 8 bits = 1 byte  B8
11942    # Length                    3 bytes          B24            # Length (in bytes) of metadata to follow (does not include the size of the METADATA_BLOCK_HEADER)
11943
11944# METADATA_BLOCK_STREAMINFO       Size          Type       - Total 34 bytes
11945
11946    # min_blocksize             2 bytes          B16            # The minimum block size (in samples) used in the stream.
11947    # min_blocksize             2 bytes          B16            # (Minimum blocksize == maximum blocksize) implies a fixed-blocksize stream.
11948    # min_framesize             3 bytes          B24            # The minimum frame size (in bytes) used in the stream. May be 0 to imply the value is not known.
11949    # max_framesize             3 bytes          B24
11950    # sample_rate              20 bits           B20            # Sample rate in Hz. Though 20 bits are available, the maximum sample rate is limited by the structure of frame headers to 1048570Hz.
11951    # Channels                  3 bits           B3             # (number of channels) - 1. FLAC supports from 1 to 8 channels
11952    # bits_per_sample           5 bits           B5             # (bits per sample) - 1. FLAC supports from 4 to 32 bits per sample. Currently the reference encoder and decoders only support up to 24 bits per sample.
11953    # Total samples            36 bits                          # 'Samples' means inter-channel sample, i.e. one second of 44.1Khz audio will have 44100 samples regardless of the number of channels.
11954    # MD5 signature            16 bytes
11955
11956## NOTES: FLAC specifies a minimum block size of 16 and a maximum block size of 65535, meaning
11957## the bit patterns corresponding to the numbers 0-15 in the minimum blocksize and maximum blocksize fields are invalid.
11958
11959}
11960
11961sub read_vorbis_comment_tag {  # to 'ogg' and 'flac' files
11962   my ($vorbis_comment, $extension_input) = @_;
11963
11964   my $Title=""; my $Artist=""; my $Album=""; my $Year=""; my $Comment=""; my $Track_Number="";
11965   my $Total_Track = ""; my $Genre="";
11966
11967   # field_name = field_value, field_value_length
11968
11969   # from vorbis-tools-1.1.1/ogginfo/ogginfo2.c
11970   # "Xiphophorus libVorbis I 20000508", "1.0 beta 1 or beta 2"
11971   # "Xiphophorus libVorbis I 20001031", "1.0 beta 3"
11972   # "Xiphophorus libVorbis I 20010225", "1.0 beta 4"
11973   # "Xiphophorus libVorbis I 20010615", "1.0 rc1"
11974   # "Xiphophorus libVorbis I 20010813", "1.0 rc2"
11975   # "Xiphophorus libVorbis I 20011217", "1.0 rc3"
11976   # "Xiphophorus libVorbis I 20011231", "1.0 rc3"
11977   # "Xiph.Org libVorbis I 20020717", "1.0"
11978   # "Xiph.Org libVorbis I 20030909", "1.0.1"
11979   # "Xiph.Org libVorbis I 20040629", "1.1.0 rc1"
11980   # "Xiph.Org libVorbis I 20070622", "1.2.0"
11981
11982
11983   # The first thing in the comment is the vendor ID size, followed by a UTF8 string with the vendor ID.
11984   my $vendor_size = substr($vorbis_comment,0,4); # 4 bytes = 32 bits
11985   $vendor_size = unpack "L",$vendor_size;
11986   #print "vendor_size = $vendor_size\n";
11987   my $vendor  = substr($vorbis_comment, 4, $vendor_size + 4);    # read the vendor
11988   #print "vendor = $vendor \n";
11989   $vorbis_comment  = substr($vorbis_comment, $vendor_size + 4);  # cut, remove
11990
11991   if ( $vendor =~ /20010615/ ){ $vendor = '1.0rc1';    }
11992   elsif ( $vendor =~ /20010813/ ){ $vendor = '1.0rc2'; }
11993   elsif ( $vendor =~ /20011217/ or $vendor =~ /20011231/ ){ $vendor = '1.0rc3'; }
11994   elsif ( $vendor =~ /20020717/ ){ $vendor = '1.0';    }
11995   elsif ( $vendor =~ /20030909/ ){ $vendor = '1.0.1';  }
11996   elsif ( $vendor =~ /20040629/ ){ $vendor = '1.1.0rc1';  }
11997   elsif ( $vendor =~ /20050304/ ){ $vendor = '1.1.1';  }
11998   elsif ( $vendor =~ /20070622/ ){ $vendor = '1.2.0';  }
11999   else { $vendor =""; }
12000   if ( $extension_input eq 'ogg' ) { $Technical_Info = "OGG Vorbis $vendor"; $metadata{technical_info}  = $Technical_Info; }
12001
12002   # Next the number of fields in the comment vector.
12003   my $item_count = substr($vorbis_comment,0,4);
12004   $item_count = unpack "L",$item_count;
12005   #print "item_count = $item_count\n";
12006   $vorbis_comment  = substr($vorbis_comment, 4);    # cut, remove
12007
12008   # Each comment field is in the format "key=value" in a UTF8 string and has
12009   # 4 bytes before the text starts that gives the length.
12010   my $tagLen;
12011   my ($tag_key, $tag_val);
12012   my %taghash = ();
12013
12014   for (my $i=0;$i<$item_count;$i++)
12015   {
12016        $tagLen = substr($vorbis_comment,0,4); # read 4 bytes for size/length
12017	$tagLen = unpack "L",$tagLen;
12018	#print "tagLen = $tagLen\n";
12019	$vorbis_comment = substr($vorbis_comment,4);        # cut, remove 4 bytes
12020
12021	my $comment = substr($vorbis_comment,0,$tagLen);    # read $tagLen bytes
12022        $vorbis_comment = substr($vorbis_comment,$tagLen);  # cut, remove $tagLen bytes
12023
12024	if ($comment =~ /^(.*?)=/) { $tag_key = $1; }  # = separation
12025	#$tag_key =~ tr/A-Z/a-z/; #lower case
12026	$tag_key = lc($tag_key);  #lower case
12027        $comment =~ s/^.*?=//;
12028
12029	$tag_val = decode("utf8",$comment);
12030
12031	#print "tag_key = $tag_key ;; tag_val = $tag_val ;; vorbis_comment = $vorbis_comment\n";
12032	# Fill the hash
12033	$taghash{$tag_key} = $tag_val;
12034	if ( length($vorbis_comment) <= 0 ){last;}
12035   }
12036   # framing bit or framing_flag
12037   #my $framing_bit = bin2dec( unpack "B8",substr($vorbis_comment,0,1) ); # read 1 byte = 8 bits
12038   #print "framing_bit = $framing_bit ;;\n";
12039
12040   if ( defined($taghash{date})    ){ $Year = $taghash{date};     }
12041   if ( defined($taghash{artist})  ){ $Artist = $taghash{artist}; }
12042   if ( defined($taghash{title})   ){ $Title = $taghash{title};   }
12043   if ( defined($taghash{genre})   ){ $Genre = $taghash{genre};   }
12044   if ( defined($taghash{album})   ){ $Album = $taghash{album};   }
12045
12046   if ( defined($taghash{tracknumber})  ){ $Track_Number = $taghash{tracknumber};   }
12047   if ( $Track_Number =~ /(\d+)\/(\d+)/ ){ $Track_Number = $1 ; $Total_Track = $2 ; }
12048
12049   if ( defined($taghash{description}) ){ $Comment = $taghash{description};                                }
12050   if ( defined($taghash{comment}) and not defined($taghash{description}) ){ $Comment = $taghash{comment}; }
12051
12052   $Track_Number = remove_change_10_chars($Track_Number);
12053   $Total_Track  = remove_change_10_chars($Total_Track);
12054
12055   # get all the 8 tags
12056   $metadata{title}   = remove_change_10_chars($Title);
12057   $metadata{artist}  = remove_change_10_chars($Artist);
12058   $metadata{album}   = remove_change_10_chars($Album);
12059   $metadata{comment} = remove_change_10_chars($Comment);
12060   $metadata{genre}   = remove_change_10_chars($Genre);
12061   $metadata{track}       = $Track_Number =~ /^\d+$/ ? sprintf("%02d", $Track_Number ) : "" ; # "%02d": leading zero
12062   $metadata{total_track} = $Total_Track  =~ /^\d+$/ ? sprintf("%02d", $Total_Track  ) : "" ;
12063   $metadata{year}        = $Year =~ /^\d{1,4}$/ ? sprintf("%04d", $Year ) : "" ;
12064
12065# See: file:///usr/share/doc/libvorbis0-devel-1.1.0/Vorbis_I_spec.html
12066# The comment header:
12067#  1) [vendor_length] = read an unsigned integer of 32 bits (= 4 bytes)
12068#  2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
12069#  3) [user_comment_list_length] = read an unsigned integer of 32 bits
12070#  4) iterate [user_comment_list_length] times {
12071#       5) [length] = read an unsigned integer of 32 bits
12072#       6) this iteration's user comment = read a UTF-8 vector as [length] octets
12073#     }
12074#  7) [framing_bit] = read a single bit as boolean
12075#  8) if ( [framing_bit] unset or end-of-packet ) then ERROR
12076#  9) done.
12077
12078}
12079
12080sub show_ogg_info {
12081   my %args = ( @_ );
12082   my $extension_input = $args{extension_input}; my $row = $args{row}; my $filepath = $args{filepath};
12083
12084   return unless $extension_input eq 'ogg';
12085
12086    my $header;
12087    my $buffer;
12088
12089    use constant OGGHEADERFLAG => 'OggS';
12090
12091    my @block_type = ( 'STREAMINFO', 'PADDING', 'APPLICATION', 'SEEKTABLE',
12092                       'VORBIS_COMMENT', 'CUESHEET', 'reserved' );
12093
12094    open(OGG, filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
12095
12096    binmode OGG;
12097    $fileSize = -s filename_from_unicode $filepath;
12098
12099    # Read ogg header.
12100    read (OGG, $header, 4); # 4 byte for 'OggS'
12101
12102    if ($header ne OGGHEADERFLAG) { return $false; }
12103
12104    ##------------------------##
12105    # Search for the first header containing Identification header ( $header_type_flag == 1 ).
12106    my $byte = 25; # at least equal to 28 bytes
12107    my $string = "";
12108    my $myseek = sub {
12109		my $n = $_[0] || 6;   # read 6 bytes for one loop to search for 'vorbis' string
12110		$byte += $_[1] || 1;  # advance 1 byte for one loop
12111		seek OGG, $byte, 0;
12112		read OGG, $string, $n;
12113    };
12114    while ($string ne 'vorbis') {
12115	&$myseek(6,1);
12116	if ($byte > 8*1024) { return $false; }
12117    }
12118    #print "byte = $byte\n";
12119    ##------------------------##
12120    my $first_frame_size = $byte - 1;  # 28 is the more likely value.
12121
12122    seek OGG, $first_frame_size, 0;    # set read to $first_frame_size ( at least equal to 28 bytes).
12123
12124    # Identification header (type = 1)
12125    read (OGG, my $header_type, 1); # read 1 byte
12126    $header_type = bin2dec(unpack "B8",$header_type);  # header_type = 1 indicates the Identification header.
12127    #print "header_type = $header_type\n";
12128    if ($header_type != 1){return;}
12129
12130    read (OGG, $header, 6); # read + 6 bytes  for 'vorbis'
12131
12132    read (OGG, my $vorbis_version, 4); # read 4 bytes = 32 bits
12133    $vorbis_version = unpack "L",$vorbis_version;
12134    if ($vorbis_version != 0){return $false;}
12135    #print "vorbis_version = $vorbis_version\n";
12136
12137    read (OGG, my $audio_channels, 1); # read 1 byte = 8 bits
12138    $audio_channels = bin2dec(unpack "B8",$audio_channels);
12139    if ($audio_channels < 1){return $false;}
12140    #print "audio_channels = $audio_channels\n";
12141
12142    read (OGG, $Frequency, 4); # read 4 bytes
12143    $Frequency = unpack "L",$Frequency;
12144    if ($Frequency < 1){return $false;}
12145    #print "Frequency = $Frequency\n";
12146
12147    read (OGG, my $bitrate_maximum, 4); # read 4 bytes
12148    $bitrate_maximum = unpack "L",$bitrate_maximum;
12149    #print "bitrate_maximum = $bitrate_maximum\n";
12150
12151    read (OGG, $bitrate_nominal, 4); # read 4 bytes
12152    $bitrate_nominal = unpack "L",$bitrate_nominal;
12153    if ($bitrate_nominal <= 0){return $false;}
12154    #print "bitrate_nominal = $bitrate_nominal\n";
12155
12156    read (OGG, my $bitrate_minimum, 4); # read 4 bytes
12157    $bitrate_minimum = unpack "L",$bitrate_minimum;
12158    #print "bitrate_minimum = $bitrate_minimum\n";
12159
12160    read (OGG, my $blocksize, 1); # read 1 byte = 8 bits = (4 + 4) bits
12161
12162    $blocksize = unpack "B8",$blocksize;
12163    #print "blocksize = $blocksize \n";
12164
12165    # "1 << x" is equal to "2 exponent to x = 2 ** x" valid for x=2^n, n=0,1,2,... .
12166    #my $exp0 = bin2dec(substr($blocksize, 4, 4));
12167    #my $exp1 = bin2dec(substr($blocksize, 0, 4));
12168    #my $blocksize_0 = 1 << $exp0; # read the first 4 bits
12169    #my $blocksize_1 = 1 << $exp1; # read the last 4 bits
12170
12171    #print "exp0 = $exp0 ;; exp1 = $exp1 ;; blocksize_0 = $blocksize_0 ;; blocksize_1 = $blocksize_1\n";
12172    #if ($blocksize_0 < 8){return $false;}
12173    #if ($blocksize_1 < $blocksize_0){return $false;}
12174
12175    # See /usr/share/doc/libvorbis0-devel-1.1.0/Vorbis_I_spec.html
12176    # Seek for the last header complete:
12177    # -The granule position of the first pages containing only headers is zero.
12178    # -The granule position of pages containing Vorbis audio is in units of PCM audio samples (per channel;
12179    #  a stereo stream's granule position does not increment at twice the speed of a mono stream).
12180    # -The granule position of a page represents the end PCM sample position of the last packet completed
12181    #  on that page. A page that is entirely spanned by a single packet (that completes on a subsequent page)
12182    #  has no granule position, and the granule position is set to '-1'.
12183
12184    # To find the length of the file see http://wiki.xiph.org/VorbisStreamLength/
12185    # Seek for the first header containing Vorbis audio.
12186
12187    my $firstGranulePosition = 0;
12188
12189    my $header_type_flag = 0;
12190    my $complete = 0; # init value
12191    $string = "";
12192    $byte = -1;
12193    $myseek = sub {
12194		my $n = $_[0] || 4;   # read 4 bytes for one loop
12195		$byte += $_[1] || 1;  # advance 1 byte for one loop
12196		seek OGG, $byte, 0;
12197		read OGG, $string, $n;
12198    };
12199    # Search for the first header containing Vorbis audio ( $header_type_flag == 1 ).
12200    # This occur when $firstGranulePosition != 0
12201    while ( $complete <= 8 and $firstGranulePosition == 0 ){
12202       while ($string ne 'OggS') {
12203	   &$myseek(4,1);
12204	   if ($byte > 32*1024) { return $false; }
12205       }
12206       $string = "";
12207       $byte += 1;
12208       $complete += 1;
12209
12210       read (OGG, my $stream_structure_version, 1);
12211       read (OGG, $header_type_flag, 1);
12212       # first page of logical bitstream (bos) when $header_type_flag = 3
12213       $header_type_flag = bin2dec(unpack "B8",$header_type_flag);
12214
12215       read (OGG, $firstGranulePosition, 8); # 8 bytes = 64 bits
12216       $firstGranulePosition = unpack "L2",$firstGranulePosition;
12217       #print "firstGranulePosition = $firstGranulePosition; ";
12218       #print "header_type_flag = $header_type_flag ; complete = $complete ; byte = $byte\n";
12219
12220       #read (OGG, my $stream_serial_number, 4); # 4 bytes
12221       #read (OGG, my $page_sequence_no, 4); # 4 bytes
12222       #$page_sequence_no = unpack "L",$page_sequence_no;
12223       #print "page_sequence_no = $page_sequence_no\n";
12224    }
12225    # remove the bytes of the first n headers without Vorbis audio.
12226    my $total_data_length = $fileSize - $byte + 1;
12227
12228    $complete = 0;
12229    $string = "";
12230    $byte = 3;
12231    $myseek = sub {
12232		my $n = $_[0] || 4;   # read 4 bytes for one loop
12233		$byte += $_[1] || 1;  # advance 1 byte for one loop
12234		seek OGG, -$byte, 2;  # Set FileHandle $byte bytes from the end of file
12235		read OGG, $string, $n;
12236    };
12237    # Search for the last header OggS containing Vorbis audio
12238    while ( $complete <= 0 ){ # 0: last header OggS
12239       while ($string ne 'OggS') {
12240	   &$myseek(4,1);
12241	   if ($byte > 32*1024) { return $false; }
12242       }
12243       #print "complete = $complete ;; byte = $byte\n";
12244       $string = "";
12245       $byte += 1;
12246       $complete += 1;
12247    }
12248    my $current_position = tell OGG;
12249    #print "byte = $byte ;; current_position = $current_position\n";
12250
12251    read (OGG, my $stream_structure_version, 1);
12252    $stream_structure_version = bin2dec(unpack "B8",$stream_structure_version);
12253    read (OGG, $header_type_flag, 1);
12254    # last page of logical bitstream (eos) when $header_type_flag = 5.
12255    $header_type_flag = bin2dec(unpack "B8",$header_type_flag);
12256    #print "header_type_flag = $header_type_flag\n";
12257
12258
12259    # 0x12 0x34 0x56 0x78     # big-endian 4 bytes
12260    # 0x78 0x56 0x34 0x12     # little-endian
12261    read (OGG, my $lastGranulePosition, 8); # 8 bytes = 64 bits
12262    $lastGranulePosition = unpack "L2",$lastGranulePosition;
12263    #print "lastGranulePosition = $lastGranulePosition\n";
12264
12265    my $Total_Samples = $lastGranulePosition ; #  - $firstGranulePosition;
12266    if ($Total_Samples < 1){return $false;}
12267
12268    my $totalSeconds = $Total_Samples / $Frequency;
12269    $bitrate_original = ( $total_data_length * 8 ) / $totalSeconds;
12270    #print "totalSeconds = $totalSeconds ;; Kbitrate_original = $bitrate_original ;; total_data_length = $total_data_length\n";
12271    $totalSeconds = sprintf("%0.0f",$totalSeconds);
12272    # convert x seconds to hour:min:sec format
12273    my ($hour,$min,$sec) = sec_to_time($totalSeconds);    # return ($hour,$min,$sec);
12274
12275    $Frequency = sprintf("%.0f", $Frequency );
12276    $mode_channel = $audio_channels;
12277    $Time_Min_Sec = $hour > 0 ? "$hour:$min:$sec" : "$min:$sec" ;
12278    $bitrate_original = sprintf("%.1f", $bitrate_original/1000 ); #Kbitrate
12279    $bitrate_nominal = sprintf("%.0f", $bitrate_nominal/1000 ); #Kbitrate
12280    $fileSize = sprintf("%.1f", $fileSize/(1024 * 1024) ); # MBytes ; 2**10 = 1024
12281    #print "fileSize = $fileSize MB \n";
12282
12283    close(OGG);
12284
12285   #$Technical_Info = "OGG Vorbis";
12286   $bitrate_average = $true;
12287
12288   # get all technical informations:
12289   # $metadata{technical_info}  = $Technical_Info; see 'sub read_vorbis_comment_tag'
12290   $metadata{bitrate}         = $bitrate_original;
12291   $metadata{bitrate_nominal} = $bitrate_nominal;  # only for ogg
12292   $metadata{bitrate_average} = $bitrate_average;  # $true or $false
12293   $metadata{frequency}       = $Frequency;
12294   $metadata{mode}            = $mode_channel;
12295   $metadata{length}          = $Time_Min_Sec if ( $extension_input ne 'cda' );
12296   $metadata{fileSize}        = $fileSize;
12297
12298    return $true;
12299
12300#  See: file:///usr/share/doc/libvorbis0-devel-1.0.1/Vorbis_I_spec.html
12301
12302#       file:///usr/share/doc/libogg-1.1.2/framing.html
12303# byte value:                                         byte value:
12304#  0  0x4f 'O'  capture_pattern                        18  0xXX LSB          page sequence no
12305#  1  0x67 'g'                                         19  0xXX
12306#  2  0x67 'g'                                         20  0xXX
12307#  3  0x53 'S'                                         21  0xXX MSB
12308#  4  0x00      stream_structure_version               22  0xXX LSB          page checksum
12309#  5  bitflags  header_type_flag                       23  0xXX
12310#  6  0xXX LSB  absolute granule position              24  0xXX
12311#  7  0xXX                                             25  0xXX MSB
12312#  8  0xXX                                             26  0x00-0xff         page_segments
12313#  9  0xXX                                             27  0x00-0xff (0-255) segment_table (containing packet lacing values)
12314# 10  0xXX
12315# 11  0xXX
12316# 12  0xXX
12317# 13  0xXX MSB
12318# 14  0xXX LSB  stream serial number
12319# 15  0xXX
12320# 16  0xXX
12321# 17  0xXX MSB
12322
12323# absolute granule position: The position specified is the total samples encoded.
12324
12325# 5  bitflags: 0x01: unset = fresh packet
12326#      	               set = continued packet
12327#	       0x02: unset = not first page of logical bitstream
12328#                      set = first page of logical bitstream (bos)
12329#	       0x04: unset = not last page of logical bitstream
12330#                      set = last page of logical bitstream (eos)
12331
12332# A Vorbis bitstream begins with three header packets. The header packets are, in order,
12333# the identification header, the comments header, and the setup header.
12334
12335# Each header packet begins with the same header fields.
12336#    1) [packet_type] : 8 bit value = 1 byte
12337#    2) 0x76, 0x6f, 0x72, 0x62, 0x69, 0x73: the characters 'v','o','r','b','i','s' as six octets
12338# Identification header is type 1, the comment header type 3 and the setup header type 5 (these types
12339# are all odd as a packet with a leading single bit of '0' is an audio packet). The packets must occur
12340# in the order of identification, comment, setup.
12341
12342#  Identification header (header_type = 1)        L: An unsigned long value (32 bits).
12343# 1) [vorbis_version] = read 32 bits as unsigned integer
12344# 2) [audio_channels] = read 8 bit integer as unsigned
12345# 3) [audio_sample_rate] = read 32 bits as unsigned integer
12346# 4) [bitrate_maximum] = read 32 bits as signed integer
12347# 5) [bitrate_nominal] = read 32 bits as signed integer
12348# 6) [bitrate_minimum] = read 32 bits as signed integer
12349# 7) [blocksize_0] = 2 exponent (read 4 bits as unsigned integer)
12350# 8) [blocksize_1] = 2 exponent (read 4 bits as unsigned integer)
12351# 9) [framing_flag] = read one bit
12352
12353# The comment header (header_type = 3):
12354# The setup header (header_type = 5):
12355
12356}
12357
12358sub show_ogg_tag {
12359   my %args = ( @_ );
12360   my $extension_input = $args{extension_input}; my $row = $args{row}; my $filepath = $args{filepath};
12361   return unless $extension_input eq 'ogg';
12362
12363    open(VORBIS, filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
12364    binmode VORBIS;
12365
12366    # To find the second 'vorbis' that contain comment header (header_type = 3)
12367    # and the third 'vorbis' to get the size of comment header.
12368    my $header_type = 0;
12369    my $find_comment_header = $false;
12370    my $vorbis_comment_size = 0;
12371    my $loop = 0; # init value
12372    my $string = "";
12373    my $byte = -1;
12374    my $myseek = sub {
12375		my $n = $_[0] || 6;   # read 6 bytes for one loop
12376		$byte += $_[1] || 1;  # advance 1 byte for one loop
12377		seek VORBIS, $byte, 0;
12378		read VORBIS, $string, $n;
12379    };
12380    while ( $loop <= 2 and  $header_type != 5 ){
12381       while ($string ne 'vorbis') {
12382	   &$myseek(6,1);
12383	   if ($byte > 8*1024) { return $false; }
12384       }
12385       # The comment header (header_type = 3):
12386       seek VORBIS, $byte -1, 0;       # goes back one byte of string 'vorbis' to read [packet_type]
12387       read (VORBIS, $header_type, 1); # read 1 byte
12388       $header_type = bin2dec(unpack "B8",$header_type);
12389       #print "header_type = $header_type ;; byte = $byte ;; loop = $loop\n";
12390
12391       # $header_type == 1 for the first 'vorbis' that contain Identification header vorbis
12392       if ($header_type == 3){ # header_type = 3 indicates comment header.
12393          $find_comment_header = $true;
12394	  $vorbis_comment_size = $byte; # get the start position of comment header
12395       }
12396       $string = "";
12397       $byte += 1;
12398       $loop += 1;
12399    }
12400    if ($find_comment_header eq $false){return $false;}
12401    seek VORBIS, $vorbis_comment_size, 0;
12402    # remove 1 (because '$byte += 1;') and 1 (packet_type) and 6 (because 'vorbis')
12403    $vorbis_comment_size = ($byte - 1 - 1 - 6 ) - $vorbis_comment_size;
12404    if ($vorbis_comment_size <= 0){return $false;}
12405    #print "vorbis_comment_size = $vorbis_comment_size  \n";
12406
12407    read (VORBIS, my $header, 6); # read + 6 bytes  for 'vorbis'
12408    #print "VORBIS = $header ;; header_type = $header_type\n";
12409
12410    # read $vorbis_comment whose length is $vorbis_comment_size.
12411    read (VORBIS, my $vorbis_comment, $vorbis_comment_size );
12412    read_vorbis_comment_tag($vorbis_comment,$extension_input);
12413    #print "vorbis_comment = $vorbis_comment;;";
12414
12415    close(VORBIS);
12416    return $true;
12417}
12418
12419
12420# only obtain info from ogg files
12421sub get_ogg_info_from_file { # not more used
12422   my $Title=""; my $Artist=""; my $Album=""; my $Year=""; my $Comment=""; my $Track_Number="";
12423   my $Total_Track = ""; my $Genre=""; my $Frequency = 0; my $mode_channel = ""; my $Time_Min_Sec = "";
12424   my $bitrate_average = $true; my $bitrate_original = 160; my $fileSize = 0;
12425
12426   my $ogginfo_path = '/usr/bin/ogginfo';
12427
12428   if ($ogginfo_path eq "") {
12429       insert_msg("None informations about \"ogg\" format because can't find \"" , "black");
12430       insert_msg("ogginfo" , "red");
12431       insert_msg("\" in executable path.\n\n" , "black");
12432       return $false;
12433   }
12434
12435   my $cmd; # command
12436
12437   $cmd = "$ogginfo_path -v \"$directory/$file_ogg\" ";
12438
12439   my $res = open (OGGINFO, filename_from_unicode "$cmd 2>&1 |" ) ;
12440
12441   ## pid process of ogginfo
12442   if( !defined($res) ) {
12443	    $status_bar->push($context_id, " Could not find ogginfo!");
12444	    return $false;
12445   }
12446
12447   $/="\r"; ## start of loop
12448   while(<OGGINFO>){
12449
12450            #New logical stream (#1, serial: 424f22ae): type vorbis
12451            #Vorbis headers parsed for stream 1, information follows...
12452            #Version: 0
12453            #Vendor: Xiph.Org libVorbis I 20030909 (1.0.1)
12454            #Channels: 2
12455            #Rate: 44100
12456
12457            #Nominal bitrate: 112.001000 Kb/s        or  Nominal bitrate: 112,001000 Kb/s
12458            #Upper bitrate: 0.320000 Kb/s
12459            #Lower bitrate: 0.064000 Kb/s
12460            #User comments section follows...
12461            #        comment=gnormalize Fix value=30
12462            #        title=Vinheta do Capeta
12463            #        artist=Almir Sater
12464            #        genre=Folk
12465            #        date=1885
12466            #        album=Instrumental
12467            #        tracknumber=3
12468            #Vorbis stream 1:
12469            #        Total data length: 814983 bytes
12470            #        Playback length: 0m:56s
12471            #        Average bitrate: 115.015600 kbps
12472            #Logical stream 1 ended
12473
12474            # discover if the bitrate of mp3 file is variable
12475
12476            if( $_ =~ /Channels?:\s+(\d+)\n/i ){ $mode_channel = $1;}
12477	    if( $_ =~ /Rate:\s+(\d+)\n/i ){
12478	              my $fq = sprintf("%0.0f", $1 );
12479		      $Frequency = $fq; }
12480	    if( $_ =~ /Nominal\sbitrate:\s+(\d+[\.,]\d+)\s.*\n/i ){
12481		      my $br = sprintf("%0.0f",number_value($1) );
12482		      $br = number_value($br);
12483		      #print (" br = $br ;; 1 = $1\n");
12484		      $bitrate_original = $br; }
12485	    if( $_ =~ /\s+Playback\slength:\s+(.*)\n/i ){ $Time_Min_Sec = $1 ; }
12486	    $Time_Min_Sec =~ s/[hms]//ig;
12487	    if( $_ =~ /\s+genre=(.*)\n/i ) { $Genre = $1 ;  }
12488	    if( $_ =~ /\s+title=(.*)\n/i ) { $Title = $1 ;  }
12489	    if( $_ =~ /\s+artist=(.*)\n/i ){ $Artist = $1 ; }
12490	    if( $_ =~ /\s+album=(.*)\n/i ) { $Album = $1 ;  }
12491	    if( $_ =~ /\s+(date|year)=(.*)\n/i ){ $Year = $2 ; }
12492	    if( $_ =~ /\s+tracknumber=\s*(\d+)\/?(\d+)?\n/i ){ $Track_Number = $1 ;   }
12493	    if( $_ =~ /\s+tracknumber=\s*(\d+)\/(\d+)\n/i ){ $Total_Track = $2 }
12494	    if( $_ =~ /\s+comment=(.*)\n/i ){ $Comment = $1 ;  }
12495	    $Technical_Info = "OGG Vorbis";
12496	    $bitrate_average = $true;
12497   }
12498   $/="\n"; ## final of loop
12499   close(OGGINFO);
12500}
12501
12502sub show_wav_info {
12503   my %args = ( @_ );
12504   my $extension_input = $args{extension_input}; my $row = $args{row}; my $filepath = $args{filepath};
12505   return unless $extension_input eq 'wav';
12506
12507    my $header;
12508    my $buffer;
12509
12510    use constant WAVHEADERFLAG => 'RIFF';
12511
12512    open(WAV, filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
12513
12514    binmode WAV;
12515
12516    $fileSize = -s filename_from_unicode $filepath;  # $fileSize = $FileLength + 8 = $DataLength + 44
12517    #print "fileSize = $fileSize\n";
12518
12519    # Read WAV header.
12520    read (WAV, $header, 4); # 4 byte for 'RIFF'
12521
12522    if ($header ne WAVHEADERFLAG) { return $false; }
12523
12524    read (WAV, my $FileLength, 4); # read 4 bytes = 32 bits
12525    $FileLength = unpack "L",$FileLength;
12526    #print "FileLength = $FileLength\n";  # $FileLength = $DataLength + 36
12527
12528    read (WAV, my $FormatTag, 8); # read 8 bytes
12529    #print "FormatTag = $FormatTag\n";
12530    if ($FormatTag ne 'WAVEfmt '){ return $false;}
12531
12532    read (WAV, my $FormatLength, 4); # read 4 bytes = 32 bits
12533    $FormatLength = unpack "L",$FormatLength;
12534    #print "FormatLength = $FormatLength\n";
12535
12536    read (WAV, my $DataFormat, 2); # read 2 byte = 16 bits
12537
12538    read (WAV, my $Channels, 2); # read 2 byte = 16 bits
12539    $Channels = unpack "S",$Channels; # 00000010 00000000
12540    #$Channels = ( bin2dec(unpack "B16",$Channels) ) >> 8 ;
12541    #$Channels =  bin2dec( substr ( (unpack "B16",$Channels) , 0 , 8 ) ) ;
12542    #print "NumChannels = $Channels\n";
12543
12544    read (WAV, $Frequency, 4); # read 4 bytes
12545    $Frequency = unpack "L",$Frequency;
12546    #print "Frequency = $Frequency\n";
12547    $Frequency = sprintf("%.0f", $Frequency ); #Kbitrate
12548
12549    read (WAV, my $BytesPerSecond, 4); # read 4 bytes
12550    $BytesPerSecond = unpack "L",$BytesPerSecond;
12551    #print "BytesPerSecond = $BytesPerSecond\n";
12552    $bitrate_original = sprintf("%.1f", ($BytesPerSecond * 8)/1000 ); #Kbitrate
12553
12554    read (WAV, my $BlockAlignment, 2); # read 2 byte = 16 bits
12555
12556    read (WAV, my $BitsPerSample, 2); # read 2 byte = 16 bits
12557    $BitsPerSample = unpack "S",$BitsPerSample; # Sample Size
12558
12559    $mode_channel = "$Channels\n($BitsPerSample bits)";
12560
12561    read (WAV, my $char, 4); # read 4 bytes
12562    #print "char = $char\n";
12563    if ($char ne 'data'){ return $false;}
12564
12565    read (WAV, my $DataLength, 4); # read 4 bytes
12566    $DataLength = unpack "L",$DataLength;
12567    # 2352 = bytes per frame, "raw" mode ;; 1 sec have 75 frames = 75 * 2352 bytes = 176400 bytes
12568    #print "DataLength = $DataLength\n";
12569
12570    my $totalSeconds = int($DataLength / $BytesPerSecond) ;
12571    #my $totalSeconds2 = sprintf("%0.3f",$DataLength / $BytesPerSecond); print "\n\$totalSeconds2 = $totalSeconds2 \n";
12572
12573    # convert x seconds to hour:min:sec format
12574    my ($hour,$min,$sec) = sec_to_time($totalSeconds);    # return ($hour,$min,$sec);
12575    $Time_Min_Sec = $hour > 0 ? "$hour:$min:$sec" : "$min:$sec" ;
12576    $fileSize = sprintf("%.1f", $fileSize/(1024 * 1024) ); # MBytes ; 2**10 = 1024
12577
12578   $Technical_Info = "WAV";
12579   $encode = 'average';
12580   $bitrate_average = $true;
12581
12582   # get all technical informations:
12583   $metadata{technical_info}  = $Technical_Info;
12584   $metadata{bitrate}         = $bitrate_original;
12585   $metadata{bitrate_nominal} = 160;               # only for ogg
12586   $metadata{bitrate_average} = $bitrate_average;  # $true or $false
12587   $metadata{frequency}       = $Frequency;
12588   $metadata{mode}            = $mode_channel;
12589   $metadata{length}          = $Time_Min_Sec if ( $extension_input ne 'cda' );
12590   $metadata{fileSize}        = $fileSize;
12591
12592# See Linux::CDROM::Cookbook and http://www.sonicspot.com/guide/wavefiles.html
12593# ------------ WAV header ----------- #
12594# total size = 44 bytes
12595#struct {
12596#   char        RiffTag[4]; 	// 'RIFF'
12597#   int32_t     FileLength;     //  Filesize - 8
12598#   char	FormatTag[8];	// 'WAVEfmt '
12599#   int32_t	FormatLength;	// 16 for PCM
12600#   int16_t	DataFormat;     // Compression Code: (1) for PCM/uncompressed raw sample values ;; (2) for Microsoft ADPCM ; ...
12601#   int16_t	NumChannels;    // Mono = 1, Stereo = 2 etc.
12602#   int32_t	SampleRate;     // 8000, 44100 etc.
12603#   int32_t	BytesPerSecond; // == SampleRate * BlockAlignment
12604#   int16_t	BlockAlignment; // == NumChannels * BitsPerSample/8
12605#   int16_t	BitsPerSample;  // 8 bits, 16 bits, ... This value specifies the number of bits used to define each sample.
12606#   char	DataTag[4];	// 'data'
12607#   int32_t	DataLength;     // == NumSamples * NumChannels * BitsPerSample/8. This is the number of bytes in the data.
12608#} header = {
12609#            "RIFF", 0, "WAVEfmt ",
12610#            16, 1, 2, 44100, 176400, 4, 16,
12611#            "data", 0
12612#};
12613# ------------ WAV header ----------- #
12614
12615# $totalSeconds = $DataLength / $BytesPerSecond;
12616# $totalSamples = $Frequency * $totalSeconds;
12617
12618}
12619
12620
12621sub read_ID3v1_mp3_tag { # not used
12622    my $fname = shift;
12623    my $filepath = $directory."/".$fname;
12624    my $buffer;
12625
12626    my ($title, $artist, $album, $year, $comment, $tracknum, $genre);
12627
12628    if ($filepath !~ /\.mp3$/i) { return $false;}
12629
12630    open(IN, filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
12631
12632    binmode IN;
12633    $fileSize = -s filename_from_unicode $filepath;
12634
12635    seek(IN,-128, 2);  # Set FileHandle 128 byte from the end of file
12636    read(IN, $buffer, 128, 0); # read 128 byte to $buffer
12637
12638    my $preamble = substr($buffer,0,3); # read 3 byte
12639    return unless $preamble eq 'TAG';
12640    $buffer = substr($buffer, 3);  # remove 3 byte
12641
12642    $tracknum = unpack "C", substr($buffer, 123, 1);
12643
12644    # get TAG to variables
12645    if ( defined($tracknum) ){
12646           ( $title, $artist, $album, $year, $comment, $tracknum, $genre ) = unpack "a30a30a30a4a28xCC", $buffer;}
12647    else { ( $title, $artist, $album, $year, $comment, $genre )            = unpack "a30a30a30a4a30C", $buffer;
12648             $tracknum = "";
12649    }
12650    print "title = $title ;; artist = $artist ;; album = $album ;; year = $year  ;; comment = $comment \n";
12651    print "track number = $tracknum ;; genre = $genre ;; fileSize = $fileSize\n";
12652    return ($title, $artist, $album, $year, $comment, $tracknum, $genre);
12653}
12654
12655sub save_ID3v1_mp3_tag {
12656    my ( $filepath, $title, $artist, $album, $year, $comment, $tracknum, $genre) = @_;
12657    my $buffer; my $ID3v1 = "";
12658
12659
12660    # see 'sub eval_to_unicode'
12661    $filepath = filename_from_unicode $filepath;         # convert filename_from_unicode to local encoding
12662    #if( ! -e $filepath ) { $filepath = $files_info[$row]{filepath_local}; }   # get original filename encoding
12663
12664
12665
12666    my $char = $id3tag_character; # encode could be 'iso-8859-1' or 'utf8'.
12667
12668    $genre = get_genre_number($genre) || '12'; # 12 Other : default
12669    #print "genre_number = $genre_number ;; Genre = $Genre\n";
12670
12671    $title = encode($char,$title);  $artist  = encode($char,$artist);
12672    $album = encode($char,$album);  $comment = encode($char,$comment);
12673    $genre = encode($char,$genre);
12674    if ( not ($year =~ /^\d+$/ or $year eq "") ){return;}
12675
12676    # See 'man perlopentut' for "+<" to specify both read and write access
12677    #open(IN, '+<', filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
12678    open(IN, '+<', $filepath ) || die "Can't open <$filepath>: $!, stopped";
12679
12680    binmode IN;
12681    seek(IN,-128, 2);          # Set FileHandle 128 byte from the end of filepath
12682    read(IN, $buffer, 128, 0); # read 128 byte to $buffer
12683
12684    my $preamble = substr($buffer,0,3); # read 3 byte
12685    if ( $preamble ne 'TAG' ){ seek(IN, 0, 2); } # go to the end of filepath
12686    else{ seek(IN,-128, 2);} #overwrite, clobber
12687
12688    # save TAG to filepath, overwrite if it exist
12689    if ( $tracknum =~ /^\d+$/ ) {
12690                $tracknum = $tracknum > 255 ? 255 : $tracknum; # must be an integer from 1 and 255
12691		$ID3v1 = pack "a3a30a30a30a4a28xCC", 'TAG', $title, $artist, $album, $year, $comment, $tracknum, $genre; }
12692    else {
12693		$ID3v1 = pack "a3a30a30a30a4a30C", 'TAG', $title, $artist, $album, $year, $comment, $genre;
12694    }
12695    syswrite ( IN, $ID3v1, 128 );
12696    close(IN);
12697}
12698
12699
12700sub read_Xing_mp3_tag { # Not used
12701    my $fname = shift;
12702    my $filepath = $directory."/".$fname;
12703    my $buffer;
12704    my $off = -1; my $byte = "" ; my $try_harder = 4096;
12705    my $frames = 0;  $fileSize = 0;
12706
12707    if ($filepath !~ /\.mp3$/i) { return $false;}
12708
12709    open(IN, filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
12710
12711    binmode IN;
12712
12713    # check first three bytes for 'ID3'
12714    seek IN, 0, 0;
12715    read IN, $buffer, 3;
12716
12717    if ($buffer eq 'ID3'){
12718       # get ID3v2 tag length from bytes 7-10
12719       my $tag_size = 10;	# include ID3v2 header size
12720       seek IN, 6, 0;
12721       read IN, $buffer, 4;
12722       $tag_size += ID3_GET_SIZE28($buffer);
12723       print "tagsize = $tag_size\n";
12724       $off = $tag_size;
12725    }
12726    # For Xing headers, see "http://home.pcisys.net/~melanson/codecs/mp3extensions.txt"
12727    # Xing headers are often added to VBR (variable bit rate) MP3
12728
12729    my $myseek = sub {
12730		my $n = $_[0] || 4;  # read 4 bytes for one loop
12731		$off += $_[1] || 1;  # advance 1 byte for one loop
12732		seek IN, $off, 0;
12733		read IN, $byte, $n;
12734    };
12735    while ($byte ne 'Xing') {
12736		&$myseek;
12737		if ($off > $try_harder) { return $false; }
12738		#print "byte = $byte ;; off = $off\n";
12739    }
12740    print "preamble_xing = $byte ;; off = $off\n";
12741
12742    &$myseek(4,4);
12743    my $flags = unpack_head($byte);
12744
12745    if ($flags & 1) {
12746		&$myseek(4,4);
12747		$frames = unpack_head($byte);
12748    }
12749    if ($flags & 2) {
12750		&$myseek(4,4);
12751		$fileSize = unpack_head($byte);
12752    }
12753
12754    print "frames = $frames ;; fileSize = $fileSize\n";
12755    $Frequency = 48000;
12756
12757    my $timePerFrame = 32*36;  #32*36 = 1152
12758    my $totalSamples = $frames * $timePerFrame;
12759    my $totalSeconds = $totalSamples/$Frequency;
12760    my $bitrate = 8 * $fileSize / $totalSeconds; # 8*$filesize = size in bit
12761
12762    $totalSeconds = sprintf("%0.0f",$totalSeconds);
12763    # convert x seconds to hour:min:sec format
12764    my ($hour,$min,$sec) = sec_to_time($totalSeconds);    # return ($hour,$min,$sec);
12765
12766    $bitrate = sprintf("%0.0f",$bitrate/1000);
12767    print "Kbitrate = $bitrate ;; time = $hour:$min:$sec ;; totalSeconds = $totalSeconds\n";
12768
12769   return $true;
12770}
12771
12772# from MPEG::MP3Info
12773sub unpack_head { unpack('l', pack('L', unpack('N', $_[0]))); }
12774
12775# from MP3::Info
12776sub read_mp3_info_tag {
12777   my %args = ( @_ );
12778   my $extension_input = $args{extension_input}; my $row = $args{row}; my $filepath = $args{filepath};
12779   return unless $extension_input eq 'mp3';
12780
12781   my $Title=""; my $Artist=""; my $Album=""; my $Year=""; my $Comment=""; my $Track_Number="";
12782   my $Total_Track = ""; my $Genre=""; my $Frequency = 0; my $mode_channel = ""; my $Time_Min_Sec = "";
12783   my $bitrate_average = $true; my $bitrate_original = 160; my $fileSize = 0;
12784
12785    $filepath = filename_from_unicode $filepath;
12786
12787    # see 'sub eval_to_unicode'
12788    #my $filepath = filename_from_unicode $files_info[$row]{filepath};         # convert filename_from_unicode to local encoding
12789    #if( ! -e $filepath ) { $filepath = $files_info[$row]{filepath_local}; }   # get original filename encoding
12790    #$filepath = eval_from_unicode( row => $row , filepath => $args{filepath} );
12791
12792
12793
12794    ## MP3::Info - Copyright (c) 1998-2005 Chris Nandor.
12795    my $mp3;
12796
12797    if ( $use_external_MP3Info  ) {               # use external MP3::Info
12798       $mp3 = MP3::Info->new($filepath);
12799       get_mp3tag($filepath);
12800       get_mp3info ($filepath);                          # The conversion to unicode will be made bellow.
12801       use_mp3_utf8($false);                         # Force MP3::Info to NOT return TAG info in UTF-8.
12802    }
12803    else {                                        # use internal MP3::Info
12804       $mp3 = MP3::Info::Internal->new($filepath);
12805       MP3::Info::Internal::get_mp3tag($filepath);
12806       MP3::Info::Internal::get_mp3info ($filepath);
12807       MP3::Info::Internal::use_mp3_utf8($false);    # Force MP3::Info to NOT return TAG info in UTF-8.
12808       MP3::Info::Internal::use_winamp_genres();
12809    }
12810
12811    # eval : see <man perlfunc>
12812    eval { $mp3->title;   }; if ($@){warn "\n$filepath: $@";} else{$Title = $mp3->title   if (defined $mp3->title);}
12813    eval { $mp3->artist;  }; unless ($@){$Artist  = $mp3->artist  if (defined $mp3->artist);}
12814    eval { $mp3->album;   }; unless ($@){$Album   = $mp3->album   if (defined $mp3->album);}
12815    eval { $mp3->year;    }; unless ($@){$Year    = $mp3->year    if (defined $mp3->year);}
12816    eval { $mp3->comment; }; unless ($@){$Comment = $mp3->comment if (defined $mp3->comment);}
12817    eval { $mp3->genre;   }; unless ($@){$Genre   = $mp3->genre   if (defined $mp3->genre);}
12818    eval { $mp3->tracknum;}; unless ($@){$Track_Number = $mp3->tracknum if (defined $mp3->tracknum);}
12819
12820
12821    # %tag_v2 is a hash with id3v2 mp3 tag
12822    my $tag_v2 = get_mp3tag($filepath, 2, 2) if $use_external_MP3Info; # get_mp3tag (FILE, tag VERSION, RAW_V2)
12823    #for (sort keys %$tag_v2){
12824    #   next unless (defined $tag_v2->{$_} and defined $MP3::Info::v2_tag_names{$_});
12825    #   printf "=== %4s (%s): %s\n", $_, $MP3::Info::v2_tag_names{$_}, $tag_v2->{$_};
12826    #   $metadata{$_} = "$tag_v2->{$_}";
12827    #}
12828    # %metadata is a hash with metadata (tag) and technical information
12829    # "\0XXX\0Some Lyrics\0" such that XXX is the Language ( print chr($Lyric) ); and remove the last NULL(s).
12830    ($metadata{'Lyrics'}    = "$tag_v2->{'USLT'}") =~ s/\0+$// if (defined $tag_v2->{'USLT'});
12831    $metadata{'Encoded by'} = "$tag_v2->{'TENC'}"              if (defined $tag_v2->{'TENC'});
12832    $metadata{'Composer'}   = "$tag_v2->{'TCOM'}"              if (defined $tag_v2->{'TCOM'});
12833    $metadata{'Length'}     = "$tag_v2->{'TLEN'}"              if (defined $tag_v2->{'TLEN'});
12834
12835
12836    if( $Track_Number =~ /(\d+)\/(\d+)/ ){ $Track_Number = $1 ; $Total_Track = $2 ; }
12837
12838    if ($Genre =~ /(\d+)/){ $Genre = $1; }   # if <genre = name (number)> --> get <genre = number>
12839
12840    # if $Genre is a number, get its reference name
12841    if ($Genre =~ /^\d+$/){ $Genre = $genre_hash{"$Genre"}; }  # $genre_hash{"32"}
12842
12843    #($Title, $Artist, $Album, $Year, $Comment, $Track_Number, $Genre) = read_ID3v1_mp3_tag($file_mp3);
12844
12845    eval { $bitrate_average = $mp3->vbr; }; unless ($@){$bitrate_average = $mp3->vbr if (defined $mp3->vbr); }
12846    eval { $Frequency = $mp3->frequency; }; unless ($@){$Frequency = $mp3->frequency if (defined $mp3->frequency); }
12847    $Frequency = sprintf("%.0f", 1000*$Frequency );
12848    if ($Frequency < 1000){warn "\nCould not read the Frequency from file: $filepath";}
12849
12850    my @channel_mode = ('stereo','joint stereo','dual channels','mono');  # mono = single channel
12851    my $mode = 0;          # (0 = stereo, 1 = joint stereo, 2 = dual channel, 3 = single channel)
12852    eval { $mode = $mp3->mode; };
12853    $mode_channel = $@ ? print "Error!" : $channel_mode[$mode];
12854
12855    eval { $mp3->time; };    unless ($@){$Time_Min_Sec = $mp3->time if (defined $mp3->time);}
12856    eval { $mp3->bitrate; }; unless ($@){$bitrate_original = $mp3->bitrate if (defined $mp3->bitrate);}
12857
12858    $fileSize = -s $filepath;
12859    $fileSize = sprintf("%.1f", $fileSize/(1024 * 1024) ); # MBytes
12860
12861    my $version = ""; my $layer = " Error";
12862    eval { $version = $mp3->version  }; unless ($@){$version = $mp3->version if (defined $mp3->version);}
12863    eval { $layer   = $mp3->layer    }; unless ($@){$layer = $mp3->layer if (defined $mp3->layer);}
12864
12865   # Must convert all input data to unicode.
12866   if (1){
12867      foreach ( $Title, $Artist, $Album, $Comment, $Genre, $Year, $Track_Number, $Total_Track,
12868              $metadata{'Lyrics'}, $metadata{'Encoded by'}, $metadata{'Composer'}, $metadata{'Length'} ){
12869
12870         if ( not utf8::is_utf8($_) and defined $_ and $_ ne "" ) {
12871            #print "Is not utf8: \"$_\".\n";
12872            eval { $_ = filename_to_unicode $_; } ; # convert to unicode and remove Wide character
12873         }
12874         #if ( utf8::is_utf8($_) and defined $_ and $_ ne "" ) { print "Is utf8: \"$_\".\n"; }
12875      }
12876   }
12877
12878    $Technical_Info = "MPEG$version/layer$layer";
12879
12880    $Track_Number = remove_change_10_chars($Track_Number);
12881    $Total_Track  = remove_change_10_chars($Total_Track);
12882
12883   # get all the 8 tags
12884   $metadata{title}   = remove_change_10_chars($Title);
12885   $metadata{artist}  = remove_change_10_chars($Artist);
12886   $metadata{album}   = remove_change_10_chars($Album);
12887   $metadata{comment} = remove_change_10_chars($Comment);
12888   $metadata{genre}   = remove_change_10_chars($Genre);
12889   $metadata{track}       = $Track_Number =~ /^\d+$/ ? sprintf("%02d", $Track_Number ) : "" ; # "%02d": leading zero
12890   $metadata{total_track} = $Total_Track  =~ /^\d+$/ ? sprintf("%02d", $Total_Track  ) : "" ;
12891   $metadata{year}        = $Year =~ /^\d{1,4}$/ ? sprintf("%04d", $Year ) : "" ;
12892
12893   # get all 7 technical informations:
12894   $metadata{technical_info}  = $Technical_Info;
12895   $metadata{bitrate}         = $bitrate_original;
12896   #$metadata{bitrate_nominal} = 160;              # only for ogg
12897   $metadata{bitrate_average} = $bitrate_average;  # $true or $false
12898   $metadata{frequency}       = $Frequency;
12899   $metadata{mode}            = $mode_channel;
12900   $metadata{length}          = $Time_Min_Sec if ( $extension_input ne 'cda' );
12901   $metadata{fileSize}        = $fileSize;
12902
12903   #print "\n read_mp3_info_tag --> \$bitrate_average = $bitrate_average\n";
12904}
12905
12906sub get_mp4_info_from_file {
12907   my %args = ( @_ );
12908   my $extension_input = $args{extension_input}; my $row = $args{row}; my $filepath = $args{filepath};
12909   return unless $extension_input eq 'mp4';
12910
12911   my $Title=""; my $Artist=""; my $Album=""; my $Year=""; my $Comment=""; my $Track_Number="";
12912   my $Total_Track = ""; my $Genre=""; my $Frequency = 0; my $mode_channel = ""; my $Time_Min_Sec = "";
12913   my $bitrate_average = $true; my $bitrate_original = 160; my $fileSize = 0;
12914
12915   if ($faad_path eq "") {
12916       insert_msg("None informations about \"mp4\" format because can't find \"" , "black");
12917       insert_msg("faad" , "red");
12918       insert_msg("\" in executable path.\n\n" , "black");
12919       return $false;
12920   }
12921   my $totalSeconds = 0;
12922   $bitrate_original = 0;
12923   $bitrate_average = $true;
12924
12925   $fileSize  = -s filename_from_unicode $filepath;
12926
12927   (my $filepath2  = $filepath ) =~ s/"/\\"/g;  # change all " characters by \"
12928   # Use 'filename_from_unicode' to convert from unicode to local encoding.
12929   $filepath2  = filename_from_unicode $filepath2;
12930   _utf8_on($filepath2);  # Don't let Perl to convert $file_in2 to unicode. See 'man Encode'.
12931
12932   my $cmd = "$faad_path -i \"$filepath2\" ";
12933
12934   my $res = open (INFO, "$cmd 2>&1 |" ) or die "Can't get info $cmd: $!";
12935
12936   while(<INFO>){
12937            # LC AAC  106.939 secs, 2 ch, 44100 Hz
12938            # or 'MAIN AAC        114.966 secs, 2 ch, 44100 Hz'
12939	    # or  'ADTS, 315.815 sec, 125 kbps, 44100 Hz' for AAC format
12940
12941            #tool: FAAC 1.24 beta (Jun  8 2004) UNSTABLE
12942            #artist: Johann Sebastian Bach nome bem grand�o m�ximo teste
12943            #title: Prelude (Suite n.1) � �
12944            #album: Suites para violoncelo n.1 e n.2
12945            #track: 3
12946            #totaltracks: 12
12947            #date: 2005
12948            #genre: Bossa Nova
12949            #comment: gnormalize Fix value=30
12950
12951	    if( $_ =~ /AAC\s+(\d+[\.,]\d+)\ssecs?,\s(\d+)\sch,\s+(\d+)\sHz\n/i ){
12952	       $Time_Min_Sec = $1; # total seconds
12953	       $mode_channel = $2;
12954	       my $fq = sprintf("%.0f", $3 );
12955	       $Frequency = $fq;
12956	       $Technical_Info = "MPEG4 MP4/M4A";
12957	    }
12958	    if( $_ =~ /ADTS,\s+(\d+[\.,]\d+)\ssecs?,\s(\d+)\skbps,\s+(\d+)\sHz\n/i ){
12959	       $Time_Min_Sec = $1; # total seconds
12960	       $bitrate_original = $2;
12961	       $mode_channel = '2';
12962	       my $fq = sprintf("%.0f", $3 );
12963	       $Frequency = $fq;
12964	       $Technical_Info = "MPEG2 AAC";
12965	    }
12966	    if( $_ =~ /^artist:\s+(.*)$/mi ){ $Artist = $1 ; } # //m: '.' matches any character except "\n". See <man perlretut>
12967	    if( $_ =~ /^title:\s+(.*)$/mi  ){ $Title  = $1 ; }
12968	    if( $_ =~ /^album:\s+(.*)$/mi  ){ $Album  = $1 ; }
12969	    if( $_ =~ /^track:\s+(\d+)$/mi ){ $Track_Number = $1 ; }
12970	    if( $_ =~ /^totaltracks:\s+(\d+)$/mi ){ $Total_Track = $1 }
12971	    if( $_ =~ /^(date|year):\s+(\d+)$/mi ){ $Year = $2 ; }
12972	    if( $_ =~ /^genre:\s+(.*)$/mi   ){ $Genre   = $1 ; }
12973	    if( $_ =~ /^comment:\s+(.*)$/mi ){ $Comment = $1 ; }
12974   }
12975   close(INFO);
12976
12977   my $bitrate = ($Time_Min_Sec <= 0) ? 0 :  ($fileSize * 8) / ($Time_Min_Sec * 1000);
12978   $fileSize = sprintf("%.1f", $fileSize/(1024 * 1024) ); # MBytes
12979   $bitrate_original = ($bitrate_original > 0) ? $bitrate_original : $bitrate;
12980   $bitrate_original = sprintf("%.0f",$bitrate_original);
12981
12982   my $TotalSeconds = sprintf("%.0f",$Time_Min_Sec);
12983   my ($hour,$min,$sec) = sec_to_time($TotalSeconds);    # return ($hour,$min,$sec);
12984   $Time_Min_Sec = $hour > 0 ? "$hour:$min:$sec" : "$min:$sec" ;
12985
12986
12987   # Must convert all input data to unicode.
12988   if (1){
12989      foreach ( $Title, $Artist, $Album, $Comment, $Genre, $Year, $Track_Number, $Total_Track ){
12990
12991         if ( not utf8::is_utf8($_) and defined $_ and $_ ne "" ) {
12992            #print "Is not utf8: \"$_\".\n";
12993            eval { $_ = filename_to_unicode $_; } ; # convert to unicode and remove Wide character
12994         }
12995         #if ( utf8::is_utf8($_) and defined $_ and $_ ne "" ) { print "Is utf8: \"$_\".\n"; }
12996      }
12997   }
12998
12999   $Track_Number = remove_change_10_chars($Track_Number);
13000   $Total_Track  = remove_change_10_chars($Total_Track);
13001
13002   # get all the 8 tags
13003   $metadata{title}   = remove_change_10_chars($Title);
13004   $metadata{artist}  = remove_change_10_chars($Artist);
13005   $metadata{album}   = remove_change_10_chars($Album);
13006   $metadata{comment} = remove_change_10_chars($Comment);
13007   $metadata{genre}   = remove_change_10_chars($Genre);
13008   $metadata{track}       = $Track_Number =~ /^\d+$/     ? sprintf("%02d", $Track_Number ) : "" ; # "%02d": leading zero
13009   $metadata{total_track} = $Total_Track  =~ /^\d+$/     ? sprintf("%02d", $Total_Track  ) : "" ;
13010   $metadata{year}        = $Year         =~ /^\d{1,4}$/ ? sprintf("%04d", $Year         ) : "" ;
13011
13012   # get all technical informations:
13013   $metadata{technical_info}  = $Technical_Info;
13014   $metadata{bitrate}         = $bitrate_original;
13015   $metadata{bitrate_nominal} = 160;               # only for ogg
13016   $metadata{bitrate_average} = $bitrate_average;  # $true or $false
13017   $metadata{frequency}       = $Frequency;
13018   $metadata{mode}            = $mode_channel;
13019   $metadata{length}          = $Time_Min_Sec if ( $extension_input ne 'cda' );
13020   $metadata{fileSize}        = $fileSize;
13021}
13022
13023$buffer->create_tag ("id3tag",      'foreground' => 'blue',    # see <man Gtk2::TextTag> for more options
13024                                     'background' => '#B8D3C1', # #B8D3C1 = 184 211 193 (RGB)
13025                                  'justification' => 'center',
13026                                      left_margin => 0,
13027				            style => 'normal',
13028				        wrap_mode => 'word',
13029					    scale => '1',
13030					   family => 'Courier',
13031					  # family => 'Terminal',
13032				          # family => 'Fixed',
13033                                    'size-points' => '11');
13034
13035
13036sub save_ID3v2_mp3_tag {
13037    my ( $filepath, $title, $artist, $album, $year, $comment, $track, $Total_Track, $genre) = @_;
13038    my $tmpfile; my $copy; my $ID3v2 = "";
13039    my $buf_size = 2048*1024;       # the bigger the faster
13040    my $need_rewrite_file = $false; # to avoid rewriting the entire file
13041
13042    use constant MP3HEADERFLAG => 'ID3';
13043
13044    my $unsync = 0; my $ext_header = 0; my $experimental = 0; my $footer = 0;
13045    # Masks for TAGS FLAGS
13046    use constant ID3FLAG_USYNC => '10000000'; # Unsynchronisation       - (10000000) binary (bite 7) (the first bit)
13047    use constant ID3FLAG_EXT   => '01000000'; # Extended header         - (01000000) binary (bite 6) (the second bit)
13048    use constant ID3FLAG_EXP   => '00100000'; # Experimental indicator  - (00100000) binary (bite 5) (the third bit)
13049    use constant ID3FLAG_FOOT  => '00010000'; # Footer present          - (00010000) binary (bite 4)
13050
13051    if ( $Total_Track ne "" and $Total_Track =~ /^\d+$/ and $Total_Track >= $track ){ $track = $track."/".$Total_Track; }
13052
13053    #my $genre_number = get_genre_number($genre);
13054    # $genre .= " [$genre_number]" if (defined $genre_number);
13055
13056    my $enc_title   = $title;
13057    my $enc_artist  = $artist;
13058    my $enc_album   = $album;
13059    my $enc_comment = $comment;
13060    my $enc_genre   = $genre;
13061    my $enc_track   = $track;
13062    my $enc_year    = $year;
13063
13064    my $enc_Encoded  = $metadata{'Encoded by'} if ($metadata{'Encoded by'});
13065    my $enc_Lyrics   = $metadata{'Lyrics'}     if ($metadata{'Lyrics'});
13066    my $enc_Composer = $metadata{'Composer'}   if ($metadata{'Composer'});
13067    my $enc_Length   = $metadata{'Length'}     if ($metadata{'Length'});
13068
13069    my $char = $id3tag_character; # encode could be 'iso-8859-1' or 'utf8'.
13070
13071    if ( 1 ) {
13072
13073       $enc_title   = encode($char,$title);
13074       $enc_artist  = encode($char,$artist);
13075       $enc_album   = encode($char,$album);
13076       $enc_comment = encode($char,$comment);
13077       $enc_genre   = encode($char,$genre);
13078       $enc_track   = encode($char,$track);
13079       $enc_year    = encode($char,$year);
13080
13081       $enc_Encoded  = encode($char,$metadata{'Encoded by'}) if ($metadata{'Encoded by'});
13082       $enc_Lyrics   = encode($char,$metadata{'Lyrics'})     if ($metadata{'Lyrics'});
13083       $enc_Composer = encode($char,$metadata{'Composer'})   if ($metadata{'Composer'});
13084       $enc_Length   = encode($char,$metadata{'Length'})     if ($metadata{'Length'});
13085    }
13086
13087    my $sz_tit = length($enc_title); # get size in byte
13088    my $sz_art = length($enc_artist);
13089    my $sz_alb = length($enc_album);
13090    my $sz_com = length($enc_comment);
13091    my $sz_gen = length($enc_genre);
13092    my $sz_tra = length($enc_track);
13093    my $sz_yea = length($enc_year);
13094
13095    my $sz_enc = length($enc_Encoded)   if ($metadata{'Encoded by'});
13096    my $sz_lyr = length($enc_Lyrics)    if ($metadata{'Lyrics'});
13097    my $sz_cps = length($enc_Composer)  if ($metadata{'Composer'});
13098    my $sz_len = length($enc_Length)    if ($metadata{'Length'});
13099
13100    my $tagSize = 0;
13101    do {
13102         use bytes;
13103	 if ($year         ne ""){ $tagSize += 10 + 1 + $sz_yea;         }   # More 10 (= header frame size) + 1 null
13104	 if ($track        ne ""){ $tagSize += 11 + $sz_tra;             }
13105         if ($genre        ne ""){ $tagSize += 11 + $sz_gen;             }
13106	 if ($artist       ne ""){ $tagSize += 11 + $sz_art;             }
13107	 if ($album        ne ""){ $tagSize += 11 + $sz_alb;             }
13108	 if ($title        ne ""){ $tagSize += 11 + $sz_tit;             }
13109	 if ($metadata{'Encoded by'}){ $tagSize += 11 + $sz_enc;             }
13110	 if ($metadata{'Composer'}  ){ $tagSize += 11 + $sz_cps;             }
13111	 if ($metadata{'Length'}    ){ $tagSize += 11 + $sz_len;             }
13112	 if ($comment  ne ""        ){ $tagSize += 10 + 1 + 3 + 1 + $sz_com; }   # 5 for <nul>XXX<nul>$comment, Language = 'XXX'
13113	 if ($metadata{'Lyrics'}    ){ $tagSize += 10 + 1 + 3 + 1 + $sz_lyr; }   # 5 for <nul>XXX<nul>$Lyric, such that Language = 'XXX'
13114    };
13115
13116    # 4.11.   Comments (see id3v2.3.0.txt)
13117    # <Header for 'Comment', ID: "COMM">
13118    # Text encoding          $xx
13119    # Language               $xx xx xx
13120    # Short content descrip. <text string according to encoding> $00 (00)
13121    #  The actual text        <full text string according to encoding>
13122
13123    # ID3v2.3.0 tags:
13124    #	'TIT2' => 'TITLE'    ;; 'TPE1' => 'ARTIST',
13125    #   'TALB' => 'ALBUM'    ;; 'TYER' => 'YEAR',
13126    #	'COMM' => 'COMMENT'  ;; 'TRCK' => 'TRACKNUM',
13127    # 	'TCON' => 'GENRE'    ;; 'USLT' => 'Unsychronized lyric/text transcription'
13128    #    TENC Encoded by
13129    #    TSSE Software/Hardware and settings used for encoding
13130    #    TLEN Length - The 'Length' frame contains the length of the audiofile in milliseconds,
13131    #    TDAT Date   - The 'Date' frame is a numeric string in the DDMM format containing the date for the recording.
13132
13133    # see <man perlopentut>
13134    open(FILE, '+<', filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
13135
13136    binmode FILE;
13137    seek(FILE, 0 , 0);
13138
13139    read FILE, my $header, 3;
13140    if ( $header ne MP3HEADERFLAG ){ $need_rewrite_file = $true; }
13141
13142    read FILE, my $bytes, 2;
13143    my ($major_version, $minor_version) = unpack 'c c', $bytes;
13144    # print " vers�o ID3v2.$major_version.$minor_version\n";
13145
13146    read FILE, my $flag, 1; # flag has 1 byte = 8 bits
13147    my @bits = split //, unpack 'b8', $flag;
13148    if ($major_version >= 3) { # for ID3v2.3 or ID3v2.4 ...
13149		$unsync       = $bits[7];   # my $buffer = unpack 'b8', $flag;  $unsync = substr($buffer, 7, 1);
13150		$ext_header   = $bits[6];
13151		$experimental = $bits[5];
13152		$footer       = $bits[4] if $major_version == 4;
13153    }
13154    #print "unsync = $unsync ;; ext_head = $ext_header;; exp = $experimental;; footer = $footer \n";
13155
13156    read FILE, my $original_sz, 4;
13157    $original_sz = $need_rewrite_file ? 512 : ID3_GET_SIZE28($original_sz); # if don't have ID3 tag
13158    #print "original size = $original_sz ;; tagSize = $tagSize\n";
13159
13160#    Overall tag ID3v2.4.0 - version structure:
13161
13162#     +-----------------------------+
13163#     |      Header (10 bytes)      |
13164#     +-----------------------------+
13165#     |       Extended Header       |
13166#     | (variable length, OPTIONAL) |
13167#     +-----------------------------+  The size is excluding the frame header ('total frame size' - 10 bytes)
13168#     |   Frames (variable length)  |  and stored as a 32 bit synchsafe integer only if version >= ID3v2.4.0
13169#     +-----------------------------+
13170#     |           Padding           |  Use padding where possible to avoid rewriting the entire
13171#     | (variable length, OPTIONAL) |  file if the metadata size changes. Paddings are null bytes.
13172#     +-----------------------------+
13173#     | Footer (10 bytes, OPTIONAL) |  Padding and a footer together are not allowed by the specifications.
13174#     +-----------------------------+
13175
13176#  ID3v2 header  - The ID3v2 tag header, which should be the first information in the file, is 10 bytes as follows:
13177#     ID3v2/file identifier      "ID3"
13178#     ID3v2 version              $03 00
13179#     ID3v2 flags                %abcd0000
13180#     ID3v2 size             4 * %0xxxxxxx
13181
13182#  The ID3v2 tag size is the sum of the byte length of the extended header, the padding and the frames after unsynchronisation.
13183#  If a footer is present this equals to ('total size' - 20) bytes, otherwise ('total size' - 10) bytes.
13184#  The ID3v2 tag size is stored as a 32 bit synchsafe integer, making a total of 28 effective bits (representing up to 256MB).
13185#  Synchsafe integers are integers that keep its highest bit (bit 7) zeroed, making seven bits out of eight available.
13186#  Thus a 32 bit synchsafe integer can store 28 bits of information.
13187#  Example: 255 (%11111111) encoded as a 16 bit synchsafe integer is 383 (%00000001 01111111).
13188
13189#  Since the ID3v2 tag doesn't contain a valid syncsignal, no software will attempt to play the tag. If, for any
13190#  reason, coincidence make a syncsignal appear within the tag it will be taken care of by the 'unsynchronisation scheme'.
13191#  Only 28 bits are used in the size description to avoid the introducuction of 'false syncsignals'.
13192
13193#  ID3v2 flags                %abcd0000
13194#  a - Unsynchronisation      (bit 7)
13195#  b - Extended header        (bit 6)
13196#  c - Experimental indicator (bit 5)
13197#  d - Footer present         (bit 4)
13198
13199    # Dont't use synchsafe integer for frame size with ID3v2.3.0!!!
13200    #my $sz_tit_pack = ID3_SET_SIZE28( $sz_tit + 1);
13201    my $sz_yea_pack = reverse pack 'L', ( $sz_yea + 1);  # The size is excluding the frame header ('total frame size' - 10 bytes)
13202    my $sz_tra_pack = reverse pack 'L', ( $sz_tra + 1);  # If the frame size >= 128, then (length != length_sync)
13203    my $sz_gen_pack = reverse pack 'L', ( $sz_gen + 1);
13204    my $sz_art_pack = reverse pack 'L', ( $sz_art + 1);
13205    my $sz_alb_pack = reverse pack 'L', ( $sz_alb + 1);  # + 1 null
13206    my $sz_tit_pack = reverse pack 'L', ( $sz_tit + 1);
13207    my $sz_enc_pack = reverse pack 'L', ( $sz_enc + 1) if (defined $sz_enc);
13208    my $sz_cps_pack = reverse pack 'L', ( $sz_cps + 1) if (defined $sz_cps);
13209    my $sz_len_pack = reverse pack 'L', ( $sz_len + 1) if (defined $sz_len);
13210    my $sz_com_pack = reverse pack 'L', ( $sz_com + 1 + 4);  # 4 for XXX<nul>
13211    my $sz_lyr_pack = reverse pack 'L', ( $sz_lyr + 1 + 4) if (defined $sz_lyr);  # 4 for XXX<nul>
13212
13213    # see <man perlfunc>
13214    # b: A bit string (ascending bit order inside each byte)
13215    # B: A bit string (descending bit order inside each byte).
13216    my $tagVersion = pack('C C', 3, 0 );              # version ID3v2.3.0 (for more compatibility)
13217    #my $tagFlags   = pack('B8', ID3FLAG_FOOT );      # has a footer - optional --
13218    my $tagFlags   = pack('B8', 0 );
13219    my $tagFlagsFrame   = pack('B8 B8', 0, 0 );
13220    my $min_size   = maximum($tagSize,$original_sz);  # print "-->min_size = $min_size\n";
13221    if ( $original_sz < $tagSize ){                   # my personal choice, min_size multiple of 512
13222       $need_rewrite_file = $true;
13223       $min_size   = int($tagSize/512) * 512 + 512;   # print "min_size = $min_size ;; tagSize = $tagSize\n";
13224    }
13225    my $padding    = $min_size - $tagSize;            # print "padding = $padding\n"; # note that, always $padding >= 1 .
13226    my $tagSize_pack  = ID3_SET_SIZE28($min_size);    # $tagSize = n * 512 for n = 1,2,3,...
13227    my $total_size = $min_size + 10;                  # + 10 (if header) + 10 (if footer)
13228
13229    $ID3v2  = pack "a3a2a1a4", 'ID3', $tagVersion, $tagFlags, $tagSize_pack;  #Header
13230       $ID3v2 .= pack "a4a4a2x1a${sz_tit}", 'TIT2', $sz_tit_pack, $tagFlagsFrame, ,$enc_title   if ($title ne "");
13231       $ID3v2 .= pack "a4a4a2x1a${sz_art}", 'TPE1', $sz_art_pack, $tagFlagsFrame, ,$enc_artist  if ($artist ne "");
13232       $ID3v2 .= pack "a4a4a2x1a${sz_alb}", 'TALB', $sz_alb_pack, $tagFlagsFrame, ,$enc_album   if ($album ne "");
13233       $ID3v2 .= pack "a4a4a2x1a${sz_gen}", 'TCON', $sz_gen_pack, $tagFlagsFrame, ,$enc_genre   if ($genre ne "");
13234       $ID3v2 .= pack "a4a4a2x1a${sz_yea}", 'TYER', $sz_yea_pack, $tagFlagsFrame, ,$enc_year    if ($year ne "");
13235       $ID3v2 .= pack "a4a4a2x1a${sz_tra}", 'TRCK', $sz_tra_pack, $tagFlagsFrame, ,$enc_track   if ($track ne "");
13236       $ID3v2 .= pack "a4a4a2x1a${sz_enc}", 'TENC', $sz_enc_pack, $tagFlagsFrame, ,$enc_Encoded             if ($metadata{'Encoded by'});
13237       $ID3v2 .= pack "a4a4a2x1a${sz_cps}", 'TCOM', $sz_cps_pack, $tagFlagsFrame, ,$enc_Composer            if ($metadata{'Composer'});
13238       $ID3v2 .= pack "a4a4a2x1a${sz_len}", 'TLEN', $sz_len_pack, $tagFlagsFrame, ,$enc_Length              if ($metadata{'Length'});
13239       $ID3v2 .= pack "a4a4a2x1a3x1a${sz_com}", 'COMM', $sz_com_pack, $tagFlagsFrame, ,'XXX', ,$enc_comment if ($comment ne "");
13240       $ID3v2 .= pack "a4a4a2x1a3x1a${sz_lyr}", 'USLT', $sz_lyr_pack, $tagFlagsFrame, ,'XXX', ,$enc_Lyrics  if ($metadata{'Lyrics'});
13241    $ID3v2 .= pack "x${padding}";
13242    #$ID3v2 .= pack "a3a2a1a4", '3DI', $tagVersion, $tagFlags, $tagSize_pack;  #Footer is optional
13243
13244    # ID3
13245
13246    if ( $need_rewrite_file eq $true ){
13247       my $n = $$;
13248       while (1) { # make one temp file
13249
13250          my $dir  = -d filename_from_unicode $directory_output ? $directory_output : $home ;
13251          $tmpfile = "$dir/gnormalize-".$n.".temp";
13252
13253          if ( open(TEMP, ">", filename_from_unicode $tmpfile ) ) { last; }
13254	  if ( not -w filename_from_unicode $tmpfile ){ $status_bar->push($context_id, " $tmpfile ".$langs{msg012} ); die " $tmpfile ".$langs{msg012}; }
13255          $n++;
13256       }
13257       syswrite(TEMP, $ID3v2, $total_size );
13258
13259       if ( $header ne MP3HEADERFLAG ){ seek(FILE, 0, 0); } # go to begining of file
13260       else { seek(FILE, $original_sz + 10, 0); }           # to ignore the old ID3tagv2 at begining of file
13261
13262       # sysread FILEHANDLE,SCALAR,LENGTH,OFFSET  -- Returns the number of bytes actually read, 0 at end of file,
13263       while ( my $ret = sysread(FILE, $copy, $buf_size) ) {
13264	  syswrite(TEMP, $copy, $ret ); # make copy from FILE and paste to TEMP file
13265       }
13266       close(FILE);
13267       close(TEMP);
13268
13269       # To delete a file, use unlink : unlink ( "$filepath" ) or die "Can't delete $filepath: $!\n";
13270       # Don't use rename (see man perlfunc) because rename c funtion fails on diferent mounted file systems.
13271       # rename($tmpfile, $filepath) || die "Can't rename temp file, leaving in $tmpfile, stopped";
13272       my @cmd = ( 'mv', '-f', $tmpfile, $filepath ); # move (overwrite) the filepath
13273       exec_cmd_system2(@cmd);
13274       #print "use the temp file ; total size = $total_size ; original_sz = $original_sz\n";
13275    }
13276    else{
13277       seek(FILE, 0 , 0);                       # go to begining of filepath
13278       syswrite ( FILE, $ID3v2, $total_size );  # syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET
13279       close(FILE);
13280       #print "total size = $total_size\n";
13281    }
13282
13283    #####-------------Print-Beautiful-Tag-------------#####
13284    print_beautiful_tag( Intro    => "ID3v2.3.0 MP3 Tag (encode: $char)",
13285                         filepath => $filepath,   Title  => $title,   Artist   => $artist,
13286			 Album    => $album,      Year   => $year,    Comment  => $comment,
13287			 Genre    => $genre,      Track  => $track,
13288                       );
13289}
13290
13291sub print_beautiful_tag {
13292    my %args = ( @_,  # argument pair list goes here
13293	       );
13294    my $intro  = $args{Intro};   my $title = $args{Title};  my $artist    = $args{Artist};
13295    my $album  = $args{Album};   my $year  = $args{Year};   my $comment   = $args{Comment};
13296    my $genre  = $args{Genre};   my $track = $args{Track};  my $filepath  = $args{filepath};
13297
13298    insert_msg("\n$intro" , "small-black");
13299
13300    $title = "Title : \"$title\" ";     $artist = "Artist  : \"$artist\"";
13301    $album = "Album : \"$album\" ";    $comment = "Comment : \"$comment\"";
13302    $genre = "Genre : \"$genre\" ";       $year = "Year    : \"$year\"";
13303
13304    ###----------------------------------------------###
13305    # floor() and fmod() - See 'man POSIX'
13306    my $Total = maximum( 50 , floor( $window->allocation->width / 10 ) ) ; # max horizontal value
13307    $Total -= 1 if ( fmod($Total,2) != 0 );     # choose $Total always an even number.
13308
13309    # get length
13310    my $l_tit = length( $title );     my $l_art = length( $artist  );
13311    my $l_alb = length( $album );     my $l_com = length( $comment );
13312    my $l_gen = length( $genre );     my $l_yea = length( $year    );
13313
13314    # get maximum from left and right side.
13315    my $max_left = maximum ($l_tit,    $l_alb);    my $max_right = maximum ($l_art,     $l_com);
13316    $max_left    = maximum ($max_left, $l_gen);    $max_right    = maximum ($max_right, $l_yea);
13317
13318    # if ($max_left + $max_right) is an odd number, then add +1 and make it an even number.
13319    $max_left  += 1 if ( fmod( $max_left + $max_right, 2 ) != 0 and $max_left < $max_right );
13320    $max_right += 1 if ( fmod( $max_left + $max_right, 2 ) != 0 and $max_left > $max_right );
13321
13322    $Total  = maximum ( $Total, $max_left + $max_right ); # $Total is always an even number.
13323
13324    my $dif_left = $max_left - floor($Total/2);    my $dif_right = $max_right - floor($Total/2);
13325    #print "\$Total = $Total ; \$max_left = $max_left ; \$max_right = $max_right ; \$dif_left = $dif_left ; \$dif_right = $dif_right\n";
13326
13327    my $size_left  = floor($Total/2);  # initial value that can be changed
13328    my $size_right = floor($Total/2);
13329
13330    if ( $dif_left > 0 and $dif_right < 0 ) { $size_left += $dif_left  ; $size_right -= $dif_left  ; }
13331    if ( $dif_left < 0 and $dif_right > 0 ) { $size_left -= $dif_right ; $size_right += $dif_right ; }
13332
13333    ###----------------------------------------------###
13334
13335    my $Intro    = "File  : \"";  # has size equal to 9 spaces
13336    my $filep    =  $filepath . "\" (Track : $track)";
13337    my $l_path   = length( $filep );
13338
13339    # set "..." like ".../file/path/for/long/filepath" if necessary.
13340    my $cut_file =  $l_path > $Total - 9 ? "..." . reduce_length_size($filep, $Total - 9 - 3) : $filep ;
13341    $Intro      .=  $cut_file;
13342
13343    ###----------------------------------------------###
13344
13345    my $Intro2   = sprintf ("%-${Total}s", $Intro   );    # s: spaces, see perlfunc - sprintf.
13346    my $title2   = sprintf ("%-${size_left}s",  $title   );    my $artist2  = sprintf ("%-${size_right}s",  $artist  );
13347    my $album2   = sprintf ("%-${size_left}s",  $album   );    my $comment2 = sprintf ("%-${size_right}s",  $comment );
13348    my $track2   = sprintf ("%-${size_left}s",  $genre   );    my $year2    = sprintf ("%-${size_right}s",  $year    );
13349
13350    insert_msg("\n$Intro2\n",         "id3tag"); #print on debug textview
13351    insert_msg("${title2}${artist2}\n", "id3tag"); # 50 + 2 + 50 = 102 spaces
13352    insert_msg("${album2}${comment2}\n","id3tag");
13353    insert_msg("${track2}${year2}",     "id3tag");
13354    insert_msg("\n", "small");
13355}
13356
13357# For 32 bit synchsafe integer - The Synchsafe integers are integers that keep
13358# its highest bit (bit 7) zeroed, making seven bits out of eight available.
13359sub ID3_SET_SIZE28 {  # 32 bit synchsafe. See <man perlop>
13360    my $size = shift;
13361    my ($a, $b, $c, $d);
13362    #  Example1: 255 (%11111111) encoded as a 16 bit synchsafe integer is 383 (%00000001 01111111).
13363    #  Example2: 128 (%10000000) encoded as a 16 bit synchsafe integer is 256 (%00000001 00000000).
13364    # The bit-shift operators (<< and >>) : 16 >> 1 = 8 , that is:  16 = (10000) in binary goes to 8 = (01000)
13365    # 255 >> 1 is equal to 127, because : 255 = (11111111)    goes to    127 = (01111111)
13366    # $x >> 1 : every bit of the value stored in $x is shifted one place to the right.
13367    # $x & $y : - The values are converted to an integer, if necessary;
13368    #           - Each bit of the left operand is compared to the corresponding bit of the right operand;
13369    #           - If a pair of corresponding bits both have the value 1, the correponding bit of the
13370    #             result is set to 1. Otherwise, the corresponding bit of the result is set to 0.
13371
13372    $d = $size & 0x7f;
13373    $c = ( $size >>  7 ) & 0x7f;  # 0x7f (hexadecinal, base 16) = 127 (decimal, base 10) = 01111111 (binary, base 2)
13374    $b = ( $size >> 14 ) & 0x7f;  # 0x7f = 127 = 01111111
13375    $a = ( $size >> 21 ) & 0x7f;
13376
13377    $size = pack "C C C C", $a, $b, $c, $d;   # $a, $b, $c and $d are in decimal base.
13378
13379    #$a = sprintf( "%08b", $a ); # convert to binary base with 8 digits
13380    #$b = sprintf( "%08b", $b ); # convert to binary base with 8 digits
13381    #$c = sprintf( "%08b", $c ); # convert to binary base with 8 digits
13382    #$d = sprintf( "%08b", $d ); # convert to binary base with 8 digits
13383    #my $temp = "$a"."$b"."$c"."$d"; # binary with 32 digits
13384    #print "decimal = ",oct( "0b$temp" ),"\n";
13385    #print "binary (32 bits) = $a $b $c $d \n";
13386
13387    return $size;
13388}
13389# ID3_SET_SIZE28(2**13 + 2**14 + 2**15); # for test
13390
13391sub ID3_GET_SIZE28 {
13392    my ($x1, $x2, $x3, $x4) = unpack("C C C C", shift );
13393    my $size = ($x4 & 0x7f) + ( ($x3 & 0x7f) << 7 ) + ( ($x2 & 0x7f) << 14 ) + ( ($x1 & 0x7f) << 21 );
13394    # my $size = $x4 + ($x3 << 7) + ($x2 << 14) + ($x1 << 21);
13395    # if $x3 = 00000001    then   $x3 << 7   =  10000000 (binary) = 128 (decimal)
13396
13397    #the same result with:
13398    #my @array = reverse unpack 'C4', shift;
13399    #foreach my $i (0 .. 3) { $size += $array[$i] * 128 ** $i; }
13400
13401    #print "x1 = $x1 ;; x2 = $x2 ;; x3 = $x3 ;; x4 = $x4 ;; size = $size\n";
13402    return $size;
13403}
13404
13405sub remove_ID3v2_mp3_tag{ #not used
13406    my $fh; my $tag_size; my $file;
13407
13408    my $buf ||= 4096*1024;  # the bigger the faster
13409    seek $fh, 0, 2;
13410    my $eof = tell $fh;
13411    my $off = $tag_size;
13412
13413    while ($off < $eof) {
13414       seek $fh, $off, 0;
13415       read $fh, my($bytes), $buf;
13416       seek $fh, $off - $tag_size, 0;
13417       print $fh $bytes;
13418       $off += $buf;
13419    }
13420    truncate $fh, $eof - $tag_size or die "Can't truncate '$file': $!";
13421}
13422
13423
13424sub show_APE_tag {
13425   my %args = ( @_ );
13426   my $extension_input = $args{extension_input}; my $row = $args{row}; my $filepath = $args{filepath};
13427   #return if (not $extension_input eq 'ape' and not $extension_input eq 'mpc' and not $extension_input eq 'wav' );
13428   return unless ( $extension_input =~ /(mpc|ape|wav)/i );
13429
13430   my $Title=""; my $Artist=""; my $Album=""; my $Year=""; my $Comment=""; my $Track_Number="";
13431   my $Total_Track = ""; my $Genre="";
13432
13433    my $APEtag; my $APEtag_footer; my $Preample;
13434    my $version; my $tag_size; my $item_count;
13435    my $tag_flags;
13436
13437    open(IN, filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
13438
13439	# read APETAGEX footer, if it exist
13440	# split /PATTERN/,EXPR,LIMIT
13441	# seek FILEHANDLE,POSITION,WHENCE
13442	seek(IN,-32, 2);  # read ( 8 + 4 * 4 + 8 ) = 32 byte from the end of file
13443	read(IN, $APEtag_footer, 32, 0);
13444
13445	$Preample = substr($APEtag_footer,0,8);
13446	if ( $Preample ne 'APETAGEX' ){ return $false; }
13447	#print "Preample = $Preample\n";
13448        $APEtag_footer = substr($APEtag_footer, 8 );
13449
13450        $version = substr($APEtag_footer,0,4);
13451        $version = unpack "L",$version;
13452        #print "version = $version\n";
13453        $APEtag_footer = substr($APEtag_footer,4);
13454
13455        $tag_size = substr($APEtag_footer,0,4);
13456        $tag_size = unpack "L",$tag_size;
13457        #print "tag_size = $tag_size bytes\n";
13458        $APEtag_footer = substr($APEtag_footer,4);
13459
13460        $item_count = substr($APEtag_footer,0,4);
13461        $item_count = unpack "L",$item_count;
13462        #print "item_count = $item_count\n";
13463        $APEtag_footer = substr($APEtag_footer,4);
13464
13465        $tag_flags = substr($APEtag_footer,0,4);
13466
13467        my @bits = split //, unpack 'b32', $tag_flags;
13468	my $Contains_Header   = $bits[31];   # @bits is an array ;; the first bit is $bits[0]
13469	my $Contains_Footer   = $bits[30];
13470	my $Is_Header         = $bits[29];
13471
13472	#print "Contains_Header = $Contains_Header ;; Contains_Footer = $Contains_Footer ;; Is_Header = $Is_Header\n";
13473
13474	seek(IN,-$tag_size, 2);  # set $tag_size bytes from the end of file
13475	read(IN, $APEtag, $tag_size - 32 , 0); # don't read the last 32 bytes
13476
13477    close(IN);
13478
13479    my $tagLen;
13480    my ($tag_key, $tag_val);
13481    my %taghash = ();
13482    my $tag_copy = $APEtag;
13483
13484    for (my $i=0;$i<$item_count;$i++){
13485        $tagLen = substr($APEtag,0,4);
13486	$tagLen = unpack "L",$tagLen;
13487        $APEtag = substr($APEtag,8);
13488	#print "tagLen = $tagLen ;; APEtag = $APEtag\n";
13489
13490	if ($APEtag =~ /^(.*?)\0/) { $tag_key = $1; }  # \0 =
13491	#$tag_key =~ tr/A-Z/a-z/; #lower case
13492	$tag_key = lc($tag_key);  #lower case
13493        $APEtag =~ s/^.*?\0//;    #remove tag_key\0
13494
13495        $tag_val = substr $APEtag, 0, $tagLen; # read
13496	$tag_val = decode("utf8",$tag_val);
13497        $APEtag  = substr $APEtag, $tagLen;    # cut, remove
13498
13499	#print "tag_key = $tag_key ;; tag_val = $tag_val \n";
13500	$taghash{$tag_key} = $tag_val;         # Fill the hash
13501	if ( length($APEtag) <= 0 ){last;}
13502    }
13503    #print "Album = ",$taghash{Album},"\n\n";
13504    #for my $key (keys %taghash) { print "$key => $taghash{$key}\n"; }
13505
13506    if ( defined($taghash{year})    ){ $Year    = $taghash{year};    }
13507    if ( defined($taghash{artist})  ){ $Artist  = $taghash{artist};  }
13508    if ( defined($taghash{title})   ){ $Title   = $taghash{title};   }
13509    if ( defined($taghash{comment}) ){ $Comment = $taghash{comment}; }
13510    if ( defined($taghash{genre})   ){ $Genre   = $taghash{genre};   }
13511    if ( defined($taghash{album})   ){ $Album   = $taghash{album};   }
13512    if ( defined($taghash{track})   ){ $Track_Number = $taghash{track}; }
13513
13514    if ( defined($taghash{'encoded by'}) ){ $metadata{'Encoded by'} = $taghash{'encoded by'}; }
13515    if ( defined($taghash{'lyrics'})     ){ $metadata{'Lyrics'}     = $taghash{'lyrics'};     }
13516    if ( defined($taghash{'composer'})   ){ $metadata{'Composer'}   = $taghash{'composer'};   }
13517
13518    if( $Track_Number =~ /(\d+)\/(\d+)/ ){ $Track_Number = $1 ; $Total_Track = $2 ; }
13519
13520   $Track_Number = remove_change_10_chars($Track_Number);
13521   $Total_Track  = remove_change_10_chars($Total_Track);
13522
13523   # get all the 8 tags
13524   $metadata{title}   = remove_change_10_chars($Title);
13525   $metadata{artist}  = remove_change_10_chars($Artist);
13526   $metadata{album}   = remove_change_10_chars($Album);
13527   $metadata{comment} = remove_change_10_chars($Comment);
13528   $metadata{genre}   = remove_change_10_chars($Genre);
13529   $metadata{track}       = $Track_Number =~ /^\d+$/ ? sprintf("%02d", $Track_Number ) : "" ; # "%02d": leading zero
13530   $metadata{total_track} = $Total_Track  =~ /^\d+$/ ? sprintf("%02d", $Total_Track  ) : "" ;
13531   $metadata{year}        = $Year =~ /^\d{1,4}$/ ? sprintf("%04d", $Year ) : "" ;
13532}
13533
13534
13535sub remove_apetag {
13536   my %args = ( @_ );
13537   my $row = $args{row}; my $filepath = $args{filepath};
13538
13539   my $Title   = $files_info[$row]{title};
13540   my $Artist  = $files_info[$row]{artist};
13541   my $Comment = $files_info[$row]{comment};
13542   my $Album   = $files_info[$row]{album};
13543   my $Genre   = $files_info[$row]{genre};
13544   my $Year    = $files_info[$row]{year};
13545   my $Track_Number = $files_info[$row]{track};
13546   my $Total_Track  = $files_info[$row]{total_track};
13547
13548   my $tag_size; my $tag_flags; my $APEfooter; my $preamble;
13549
13550   print "remove_apetag --> Could not find the file: \"$filepath\"\n" if( ! -e filename_from_unicode $filepath );
13551   if (not -s filename_from_unicode $filepath) { $@ = "File is empty"; return undef; }
13552
13553   # see <man perlopentut>
13554   open(APE, "+<", filename_from_unicode $filepath ) || die "Can't open <$filepath>: $!, stopped";
13555
13556   binmode APE;
13557
13558   seek(APE,-32, 2);  # Set FileHandle 32 byte from the end of file
13559   read(APE, $APEfooter, 32, 0); # read 32 byte to $APEfooter
13560
13561   $preamble = substr($APEfooter,0,8);
13562   $preamble = unpack "a8",$preamble;
13563   if ( $preamble ne 'APETAGEX' ){ return; }
13564   #print "preamble = $preamble\n";
13565   $APEfooter = substr($APEfooter,8 + 4);  # + 4 byte (version)
13566
13567   $tag_size = substr($APEfooter,0,4);
13568   $tag_size = unpack "L",$tag_size;
13569   $APEfooter = substr($APEfooter,4 + 4);  # + 4 byte (Item Counts)
13570   #print "tag_size = $tag_size\n";
13571
13572   $tag_flags = substr($APEfooter,0,4);
13573
13574   my @bits = split //, unpack 'b32', $tag_flags;
13575   my $Contains_Header   = $bits[31];   # @bits is an array ;; the first bit is $bits[0]
13576   my $Contains_Footer   = $bits[30];
13577   my $Is_Header         = $bits[29];
13578
13579   #print "Contains_Header = $Contains_Header ;; Contains_Footer = $Contains_Footer ;; Is_Header = $Is_Header\n";
13580
13581   if ($Contains_Header){ $tag_size = $tag_size + 32; } # 32 bytes of headear
13582
13583   seek (APE, -$tag_size, 2);
13584   my $file_size = tell APE;  # file_size - APEtag_size : get the current position
13585
13586   #read(APE, $APEfooter, $tag_size, 0);
13587   #print "tell = $file_size  ;; APEtag = $APEfooter\n";
13588
13589   truncate APE, $file_size or warn "Can't truncate \"$filepath\": $!";
13590
13591   close(APE);
13592
13593   # see <man perlfunc>
13594   # tell    Returns the current position in bytes for FILEHANDLE,
13595   # truncate FH,LENGTH : Truncates the file opened on FILEHANDLE to the specified length.
13596   # sysread FILEHANDLE,SCALAR,LENGTH,OFFSET  ;; Returns the number of bytes actually read
13597   # syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET
13598
13599   #print "\n ---> remove_apetag ;; \$row = $row ;;  \$filepath = $filepath ;; \$Title = $Title\n";
13600}
13601
13602sub write_apetag { # 18Mai2008
13603   my %args = ( @_ );
13604   my $row = $args{row}; my $filepath = $args{filepath};
13605
13606   my $Title   = $files_info[$row]{title};
13607   my $Artist  = $files_info[$row]{artist};
13608   my $Comment = $files_info[$row]{comment};
13609   my $Album   = $files_info[$row]{album};
13610   my $Genre   = $files_info[$row]{genre};
13611   my $Year    = $files_info[$row]{year};
13612   my $Track_Number = $files_info[$row]{track};
13613   my $Total_Track  = $files_info[$row]{total_track};
13614
13615   my $Track;
13616   if ( $Total_Track ne "" and $Total_Track =~ /^\d+$/ and $Total_Track >= $Track_Number ){ $Track = $Track_Number."/".$Total_Track; }
13617   else { $Track = $Track_Number; }
13618
13619   my $char = 'utf8'; # encode to utf8
13620
13621   my $enc_Title   = encode($char,$Title);
13622   my $enc_Artist  = encode($char,$Artist);
13623   my $enc_Album   = encode($char,$Album);
13624   my $enc_Comment = encode($char,$Comment);
13625   my $enc_Genre   = encode($char,$Genre);
13626   my $enc_Track   = encode($char,$Track);
13627   my $enc_Year    = encode($char,$Year);
13628
13629   my $enc_Composer = encode($char,$metadata{'Composer'})   if ($metadata{'Composer'});
13630   my $enc_Encoded  = encode($char,$metadata{'Encoded by'}) if ($metadata{'Encoded by'});
13631   my $enc_Length   = encode($char,$metadata{'Length'})     if ($metadata{'Length'});
13632   my $enc_Lyrics   = encode($char,$metadata{'Lyrics'})     if ($metadata{'Lyrics'});
13633
13634   my $tail; my $tag;  my $tag_size;
13635
13636   # Masks for TAGS FLAGS
13637   use constant Contains_Header => 0x80000000; # hexdecimal 0x80 = 128 decimal = (10000000) binary (bite 8)
13638   use constant Contains_Footer => 0x40000000; # hexdecimal 0x40 =  64 decimal = (01000000) binary (bite 7)
13639   use constant Is_Header       => 0x20000000; # hexdecimal 0x20 =  32 decimal = (00100000) binary (bite 6)
13640
13641   print "write_apetag --> Could not find the file: \"$filepath\"\n" if( ! -e filename_from_unicode $filepath );
13642   if (not -s filename_from_unicode $filepath ) { print "File <$filepath> is empty: $@"; return undef; }
13643
13644   open(APETAG, ">>", filename_from_unicode $filepath ) or die "Can't open <$filepath>: $!, stopped";
13645   binmode APETAG;
13646
13647   seek APETAG, 0, 2; # go to end of file : 2 to set it to EOF (End Of File)
13648
13649   my $sz_tit = length($enc_Title);   # get size in byte
13650   my $sz_art = length($enc_Artist);
13651   my $sz_alb = length($enc_Album);
13652   my $sz_com = length($enc_Comment);
13653   my $sz_gen = length($enc_Genre);
13654   my $sz_tra = length($enc_Track);
13655   my $sz_yea = length($enc_Year);
13656
13657   my $sz_cps = length($enc_Composer)  if ($metadata{'Composer'});
13658   my $sz_enc = length($enc_Encoded)   if ($metadata{'Encoded by'});
13659   my $sz_len = length($enc_Length)    if ($metadata{'Length'});
13660   my $sz_lyr = length($enc_Lyrics)    if ($metadata{'Lyrics'});
13661
13662   my $tagTotalSize = 32;  # + 32 bytes (Footer)
13663   my $items = 0;
13664   do {
13665      use bytes;
13666      if ($enc_Title     ne ""){ $items ++; $tagTotalSize += 8 + length('Title') + 1 + $sz_tit; }
13667      if ($enc_Artist    ne ""){ $items ++; $tagTotalSize += length('Artist')  + $sz_art + 9;   }
13668      if ($enc_Album     ne ""){ $items ++; $tagTotalSize += length('Album')   + $sz_alb + 9;   }
13669      if ($enc_Comment   ne ""){ $items ++; $tagTotalSize += length('Comment') + $sz_com + 9;   }
13670      if ($enc_Genre     ne ""){ $items ++; $tagTotalSize += length('Genre')   + $sz_gen + 9;   }
13671      if ($enc_Track     ne ""){ $items ++; $tagTotalSize += length('Track')   + $sz_tra + 9;   }
13672      if ($enc_Year      ne ""){ $items ++; $tagTotalSize += length('Year')    + $sz_yea + 9;   }
13673
13674      if ($metadata{'Composer'}  ){ $items ++; $tagTotalSize += length('Composer')   + $sz_cps + 9; }
13675      if ($metadata{'Encoded by'}){ $items ++; $tagTotalSize += length('Encoded by') + $sz_enc + 9; }
13676      if ($metadata{'Length'}    ){ $items ++; $tagTotalSize += length('Length')     + $sz_len + 9; }
13677      if ($metadata{'Lyrics'}    ){ $items ++; $tagTotalSize += length('Lyrics')     + $sz_lyr + 9; }
13678   };
13679
13680   # L: An unsigned long value. --- see <man perlfunc>
13681   my $tagVersion      = pack('L', 2000 ); # 2000: version
13682   $tagTotalSize       = pack('L', $tagTotalSize );  #  footer + all tag items
13683   my $tagTotalItems   = pack('L', $items ); # 7 items : year,track,artist,...,comment
13684   my $tagGlobalFlags  = pack('L', Is_Header + Contains_Header + Contains_Footer );
13685   my $tagGlobalFlags2 = pack('L', Contains_Header + Contains_Footer );
13686
13687   # initial - header
13688   # APETAGEX�
13689
13690   print APETAG pack "a8a4a4a4a4x8", 'APETAGEX', $tagVersion, $tagTotalSize, $tagTotalItems, $tagGlobalFlags;
13691
13692      if ($enc_Title    ne ""){ print APETAG pack "a8a6a${sz_tit}", pack('L', $sz_tit ), 'Title',   $enc_Title;   }
13693      if ($enc_Artist   ne ""){ print APETAG pack "a8a7a${sz_art}", pack('L', $sz_art ), 'Artist',  $enc_Artist;  }
13694      if ($enc_Album    ne ""){ print APETAG pack "a8a6a${sz_alb}", pack('L', $sz_alb ), 'Album',   $enc_Album;   }
13695      if ($enc_Comment  ne ""){ print APETAG pack "a8a8a${sz_com}", pack('L', $sz_com ), 'Comment', $enc_Comment; }
13696      if ($enc_Genre    ne ""){ print APETAG pack "a8a6a${sz_gen}", pack('L', $sz_gen ), 'Genre',   $enc_Genre;   }
13697      if ($enc_Track    ne ""){ print APETAG pack "a8a6a${sz_tra}", pack('L', $sz_tra ), 'Track',   $enc_Track;   }
13698      if ($enc_Year     ne ""){ print APETAG pack "a8a5a${sz_yea}", pack('L', $sz_yea ), 'Year',    $enc_Year;    }
13699
13700      if ($metadata{'Composer'}  ){ print APETAG pack "a8a9a${sz_cps}", pack('L', $sz_com ), 'Composer',   $enc_Composer;}
13701      if ($metadata{'Encoded by'}){ print APETAG pack "a8a11a${sz_enc}",pack('L', $sz_enc ), 'Encoded by', $enc_Encoded; }
13702      if ($metadata{'Length'}    ){ print APETAG pack "a8a7a${sz_len}", pack('L', $sz_len ), 'Length',     $enc_Length;  }
13703      if ($metadata{'Lyrics'}    ){ print APETAG pack "a8a7a${sz_lyr}", pack('L', $sz_lyr ), 'Lyrics',     $enc_Lyrics;  }
13704
13705   print APETAG pack "a8a4a4a4a4x8", 'APETAGEX', $tagVersion, $tagTotalSize, $tagTotalItems, $tagGlobalFlags2;
13706
13707   # final - footer
13708   #APETAGEX�
13709
13710# from taglib-1.3.1: taglib/ape/ape-tag-format.txt  --- Member of APE Tag 2.0
13711# http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html
13712#/===========================================================================\
13713#| Preamble       | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' }     |
13714#|----------------|---------|------------------------------------------------|
13715#| Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000     |
13716#|----------------|---------|------------------------------------------------|
13717#| Tag Size       | 4 bytes | Tag size in bytes including footer and all tag |
13718#|                |         | items excluding the header (for 1.000          |
13719#|                |         | compatibility)                                 |
13720#|----------------|---------|------------------------------------------------|
13721#| Item Count     | 4 bytes | Number of items in the tag                     |
13722#|----------------|---------|------------------------------------------------|
13723#| Tag Flags      | 4 bytes | Global flags                                   |
13724#|----------------|---------|------------------------------------------------|
13725#| Reserved       | 8 bytes | Must be zeroed                                 |
13726#\===========================================================================/
13727
13728
13729#                           APE Tag Flags
13730#/=================================================================\
13731#| Contains Header | Bit 31      | 1 - has header | 0 - no header  |
13732#|-----------------|-------------|---------------------------------|
13733#| Contains Footer | Bit 30      | 1 - has footer | 0 - no footer  |
13734#|-----------------|-------------|---------------------------------|
13735#| Is Header       | Bit 29      | 1 - is header  | 0 - is footer  |
13736#|-----------------|-------------|---------------------------------|
13737#| Undefined       | Bits 28 - 3 | Undefined, must be zeroed       |
13738#|-----------------|-------------|---------------------------------|
13739#| Encoding        | Bits 2 - 1  | 00 - UTF-8                      |
13740#|                 |             | 01 - Binary Data *              |
13741#|                 |             | 10 - External Reference **      |
13742#|                 |             | 11 - Reserved                   |
13743#|-----------------|-------------|---------------------------------|
13744#| Read Only       | Bit 0       | 1 - read only  | 0 - read/write |
13745#\=================================================================/
13746
13747# http://www.hydrogenaudio.org/musepack/klemm/www.personal.uni-jena.de/%7Epfk/mpp/sv8/apevalue.html
13748# APE Tags Header  32 byte
13749# APE Tag Item 1   10 byte   : APE tags items should be sorted ascending by size.
13750# APE Tag Item 2   10 byte
13751# ...
13752# APE Tag Item n   10 byte
13753# APE Tags Footer  32 byte
13754
13755
13756#                          Item:  length key value
13757# APE Tag Item:   4 byte : Length len of the assigned value in bytes
13758#	    	  4 byte : Item flags
13759#	    	  m byte : Item key, can contain ASCII characters from 0x20 (Space) up to 0x7E (Tilde)
13760#	    	  1 byte : Item key terminator   0x00
13761#	    	  len byte : Item value, can be binary data or UTF-8 string
13762
13763   close(APETAG);
13764   #print "size = ",bin2dec(unpack('B32', '' )),"\n";	#219
13765   #print "genre = $Genre ;; size = $sz_gen \n";
13766   #$tagGlobalFlags2 = bin2dec( unpack('B32', '�' ) );
13767   #$tagGlobalFlags2 =  pack('L', TF_HAS_HEADER );
13768   #$tagGlobalFlags2 = bin2dec( unpack('B32', $tagGlobalFlags2 ) );
13769   #print "tagGlobalFlags2 = $tagGlobalFlags2 ;;\n";
13770
13771   #print "\n ---> write_apetag ;; \$row = $row ;; \$filepath = $filepath ;; \$Title = $Title ;; \$Album = $Album\n";
13772
13773    #####-------------Print-Beautiful-Tag-------------#####
13774    print_beautiful_tag( Intro    => "APE Tag 2.0 (encode: utf8)",
13775                         filepath => $filepath,  Year     => $Year,
13776			 Title    => $Title,     Artist   => $Artist,
13777			 Album    => $Album,     Comment  => $Comment,
13778			 Genre    => $Genre,     Track    => $Track,
13779                       ); # if ( $extension_input !~ /(wav)/ );
13780}
13781
13782sub bin2dec {
13783	# see <man perlfunc>
13784	return unpack ('N', pack ('B32', substr(0 x 32 . shift, -32)));
13785}
13786
13787
13788sub get_genre_number{
13789    my $genre = shift;
13790    my $genre_number; # 12 Other : default
13791
13792    if ( $genre =~ /^\d+$/ ){  # if $Genre is a number
13793        $genre_number = $genre;
13794    }
13795    else{ # if $genre is a reference name, get its number
13796        for my $key (keys %genre_hash) {
13797           # print "$key => $genre_hash{$key}\n";
13798	   if ( uc($genre_hash{$key}) eq uc($genre) ){
13799	      $genre_number = $key;
13800	      last;
13801	   }
13802        }
13803    }
13804    return $genre_number;
13805}
13806
13807
13808sub ppaths{ # print all selected paths
13809  my $tree_selection = $treeview_play->get_selection;
13810  my $model = $treeview_play->get_model;
13811  my @paths = $tree_selection->get_selected_rows; # sel = Gtk2::TreePath
13812
13813  foreach my $path (@paths) {
13814     $iter = $model->get_iter($path);
13815     next unless $iter;
13816
13817     my $selected = $model->get_value($iter,COLUMN_FILE); # track number
13818     my $row = $selected - 1;  # $track-1;
13819     my $filepath = $files_info[$row]{filepath};
13820     my $extension_input = $files_info[$row]{extension_input};
13821     my $title = $files_info[$row]{title};
13822     print "\$filepath = $filepath ; \$extension_input = $extension_input ; \$title = $title\n";
13823  }
13824}
13825
13826# save_tag button of table31
13827sub save_tag {  # sub cell_edited {
13828
13829   # get selected row  from 'sub get_selection_tree' that will be used by 'sub change_tag' and 'sub save_tag'
13830   my $row = $files_info[0]{get_selection_row};
13831
13832   return unless defined $row;
13833
13834   my $filepath = $files_info[$row]{filepath};
13835   my $extension_input = $files_info[$row]{extension_input};
13836   my %hash = determine_directory_and_filename_and_extension(row => $row);
13837
13838   my $cmd;
13839
13840   # the hash %temp_tags has temporary variables only used on 'sub change_tag'.
13841   $files_info[$row]{title}       = $temp_tags{title};
13842   $files_info[$row]{artist}      = $temp_tags{artist};
13843   $files_info[$row]{album}       = $temp_tags{album};
13844   $files_info[$row]{comment}     = $temp_tags{comment};
13845   $files_info[$row]{genre}       = $temp_tags{genre};
13846   $files_info[$row]{track}       = $temp_tags{track};
13847   $files_info[$row]{total_track} = $temp_tags{total_track};
13848   $files_info[$row]{year}        = $temp_tags{year};
13849
13850   Set_this_changes_on_Treeview($filepath,$row)     if ($files_info[$row]{extension_input} ne 'cda');
13851   Set_this_changes_on_Treeview_cda($filepath,$row) if ($files_info[$row]{extension_input} eq 'cda');
13852
13853   my $Title   = $temp_tags{title};
13854   my $Artist  = $temp_tags{artist};
13855   my $Comment = $temp_tags{comment};
13856   my $Album   = $temp_tags{album};
13857   my $Genre   = $temp_tags{genre};
13858   my $Year    = $temp_tags{year};
13859   my $Track_Number = $temp_tags{track};
13860   my $Total_Track  = $temp_tags{total_track};
13861
13862   #print "\n sub save_tag --->1 \$row = $row \$filepath = $filepath ;; \$Title = $Title ;; \$Album = $Album\n";
13863
13864    #verify if the choosed file is valid
13865    if ( $extension_input eq 'mp3' ){
13866
13867        save_ID3v2_mp3_tag($filepath, $Title, $Artist, $Album, $Year, $Comment, $Track_Number, $Total_Track, $Genre );
13868
13869        save_ID3v1_mp3_tag($filepath, $Title, $Artist, $Album, $Year, $Comment, $Track_Number, $Genre );
13870    }
13871    if ( $extension_input eq 'ogg' ){
13872       if ($vorbiscomment_path eq "") {
13873          $status_bar->push($context_id, " vorbiscomment can't be found!");
13874          return $false;
13875       }
13876       my   @cmd_tag = ( $vorbiscomment_path, '--raw', '-t', "title=$Title", '-t', "artist=$Artist" );
13877       push @cmd_tag,  ( '-t', "genre=$Genre", '-t', "date=$Year", '-t', "album=$Album" );
13878       if ( $Total_Track ne "" and $Total_Track >= $Track_Number ) {
13879               push @cmd_tag, ( '-t', "tracknumber=$Track_Number/$Total_Track" ); }
13880       else { push @cmd_tag, ( '-t', "tracknumber=$Track_Number" );              }
13881       push @cmd_tag,  ( '-t', "description=$Comment", '-t', "comment=$Comment", '-w', filename_from_unicode $filepath );
13882
13883       exec_cmd_system3(@cmd_tag);
13884    }
13885    if ( $extension_input eq 'flac' ){
13886       if ($metaflac_path eq "") {
13887          $status_bar->push($context_id, " metaflac can't be found!");
13888          return $false;
13889       }
13890       exec_cmd_system2( $metaflac_path, '--remove-all-tags', $filepath );
13891
13892       # tag commands -- to see the entries: metaflac --list music.flac
13893       # obsolete,  deprecated: --show-vc-field=Title ;; --set-vc-field=Title=
13894       # --no-utf8-convert  decode("utf8",$1)
13895
13896       my   @cmd = ( $metaflac_path, '--no-utf8-convert', "--set-tag=Title=$Title", "--set-tag=Artist=$Artist" );
13897       push @cmd,  ( "--set-tag=Album=$Album", "--set-tag=Date=$Year", "--set-tag=Description=$Comment", "--set-tag=Comment=$Comment" );
13898       if ($Total_Track ne "" and $Total_Track >= $Track_Number){
13899       	      push @cmd, ( "--set-tag=Tracknumber=$Track_Number/$Total_Track" ); }
13900       else { push @cmd, ( "--set-tag=Tracknumber=$Track_Number" );              }
13901       push @cmd, ( "--set-tag=Genre=$Genre", filename_from_unicode $filepath );
13902
13903       exec_cmd_system3(@cmd);
13904       #print "\n save_tag --> \@cmd = @cmd\n";
13905    }
13906    if ( $extension_input =~ /(mpc|ape|wav)/ ){
13907       remove_apetag (%hash);
13908       write_apetag (%hash);
13909    }
13910
13911    my $info_cddb = "";
13912    if ( $extension_input eq 'cda' ){
13913       $info_cddb = save_cddb_info_to_home($row); # save info to "$home/.cddb/ ($dir/) $cddbid"
13914       $status_bar->push($context_id, " Saved tag to $info_cddb");
13915    }
13916    else{ $status_bar->push($context_id, " $extension_input".$langs{msg062} ); }
13917
13918    $button_save_tag->set_sensitive($false);
13919
13920    #print "\n sub save_tag --->2 \$row = $row \$filepath = $filepath ;; \$Title = $Title ;; \$Album = $Album\n";
13921}
13922
13923sub Set_this_changes_on_Treeview {
13924   my ($filepath,$row) = @_;
13925   my $model = $treeview_play->get_model;
13926
13927   my $Title   = $files_info[$row]{title};
13928   my $Artist  = $files_info[$row]{artist};
13929   my $Comment = $files_info[$row]{comment};
13930   my $Album   = $files_info[$row]{album};
13931   my $Genre   = $files_info[$row]{genre};
13932   my $Year    = $files_info[$row]{year};
13933   my $Track_Number = $files_info[$row]{track};
13934   my $Total_Track  = $files_info[$row]{total_track};
13935
13936   my @rows_with_same_files = ();
13937   for (my $row2 = 0; $row2 <= $#files_info; $row2++){
13938       if ($filepath eq $files_info[$row2]{filepath}){ push @rows_with_same_files, $row2; }
13939   }
13940   # print "\@rows_with_same_files = @rows_with_same_files ; \$filepath = $filepath\n";
13941
13942   foreach my $row2 (@rows_with_same_files){
13943      my ($iter, $path) = get_iter_path_given_track_art_alb ( value => $row2 + 1, treeview => 'treeview_play', column => COLUMN_FILE );
13944
13945      # set all the new 8 entries:
13946      $files_info[$row2]{title}   = $Title;
13947      $files_info[$row2]{artist}  = $Artist;
13948      $files_info[$row2]{album}   = $Album;
13949      $files_info[$row2]{genre}   = $Genre;
13950      $files_info[$row2]{comment} = $Comment;
13951      $files_info[$row2]{year}    = $Year;
13952      $files_info[$row2]{track}       = $Track_Number;
13953      $files_info[$row2]{total_track} = $Total_Track;
13954
13955      $model->set ($iter, COLUMN_ARTIST, $files_info[$row2]{artist}, # set value on model
13956                          COLUMN_TITLE,  $files_info[$row2]{title},
13957		          COLUMN_ALBUM,  $files_info[$row2]{album},
13958		          COLUMN_TRACK,  $files_info[$row2]{track},  # $Track_Number,
13959		          COLUMN_YEAR,   $files_info[$row2]{year},
13960	          ) if $iter;
13961   }
13962}
13963
13964sub Set_this_changes_on_Treeview_cda {
13965   my ($filepath,$row) = @_;
13966   my $model = $treeview_play->get_model;
13967
13968   my $Title   = $files_info[$row]{title};
13969   my $Artist  = $files_info[$row]{artist};
13970   my $Comment = $files_info[$row]{comment};
13971   my $Album   = $files_info[$row]{album};
13972   my $Genre   = $files_info[$row]{genre};
13973   my $Year    = $files_info[$row]{year};
13974   my $Track_Number = $files_info[$row]{track};
13975   my $Total_Track  = $files_info[$row]{total_track};
13976
13977   #print "\n Set_this_changes_on_Treeview_cda -->1 \$Title = $Title ;; \$Artist = $Artist ;; \$Album = $Album\n";
13978
13979   # initial values
13980   if ( $Artist eq "" or not defined $Artist ) { $Artist = "artist"; }
13981   if ( $Album  eq "" or not defined $Album  ) { $Album  = "album";  }
13982   if ( $Genre  eq "" or not defined $Genre  ) { $Genre  = "Other";  }
13983   if ( $Year   eq "" or not defined $Year   ) { $Year   = "";       }
13984
13985   for (my $row2 = 0; $row2 <= $#files_info; $row2++){
13986      if ($files_info[$row2]{extension_input} ne 'cda'){last;}
13987      my ($iter, $path) = get_iter_path_given_track_art_alb ( value => $row2 + 1, treeview => 'treeview_play', column => COLUMN_FILE );
13988
13989      # set all the new 8 entries:
13990      $files_info[$row2]{title}   = $Title  if ( $row2 == $row );  # change only the selected row
13991      $files_info[$row2]{artist}  = $Artist if ( $row2 == $row or $files_info[$row2]{artist} eq 'artist' );
13992      $files_info[$row2]{album}   = $Album;
13993      $files_info[$row2]{genre}   = $Genre;
13994      $files_info[$row2]{comment} = $Comment;
13995      $files_info[$row2]{year}    = $Year;
13996      #$files_info[$row2]{track}       = $Track_Number;
13997      #$files_info[$row2]{total_track} = $Total_Track;
13998
13999      $model->set ($iter, COLUMN_ARTIST, $files_info[$row2]{artist}, # set value on model
14000                          COLUMN_TITLE,  $files_info[$row2]{title},
14001		          COLUMN_ALBUM,  $files_info[$row2]{album},
14002		          #COLUMN_TRACK, $files_info[$row2]{track},  # $Track_Number,
14003		          COLUMN_YEAR,   $files_info[$row2]{year},
14004	          ) if $iter;
14005   }
14006   #print "\n Set_this_changes_on_Treeview_cda -->2 \$Title = $Title ;; \$Artist = $Artist ;; \$Album = $Album\n";
14007}
14008
14009#####-----------------------------------------------#####
14010
14011# get_info_from_entries from entry "Tag" of gnormalize for wave files
14012sub get_info_from_entries { # no more used!
14013    # get mp3/mp4 ... info to be saved on normalized file
14014    $Title = $entry_title->get_text;
14015    $Artist = $entry_artist->get_text;
14016    $Album = $entry_album->get_text;
14017    $Year = $entry_year->get_text;
14018
14019    $Track_Number = ""; $Total_Track = "";
14020    if ( $entry_tn->get_text =~ /(\d+)/ ) { $Track_Number = $1; }
14021    if ( $entry_tt->get_text =~ /(\d+)/ ) { $Total_Track = $1; }
14022
14023    #print " tn = $Track_Number ;; Total_Track = $Total_Track \n";
14024    $Comment = $entry_comment->get_text;
14025    $Genre = $ComboBoxEntry_genre->child->get_text;
14026}
14027
14028#####-----------------------------------------------#####
14029
14030##--------------------- Character ---------------------##
14031
14032# substitute the command <system()> by this sub routine <exec_cmd_system()>
14033# see man Encode. utf8, PerlIO, perlrun, ...
14034sub exec_cmd_system { # need capture the output
14035   my $cmd = shift;   # $cmd is a string
14036   my $output;
14037
14038   open (CM, filename_from_unicode "$cmd 2>&1 |" ) or die "couldn't execute \"$cmd\": $!";
14039      $/="\r"; ## start of loop
14040      while(<CM>){ $output = $_; } # $_ is the output of command
14041      $/="\n"; ## final of loop
14042   close(CM);
14043   return $output;
14044}
14045
14046sub exec_cmd_system2 {
14047   my @exec_argv = @_;
14048
14049   #$SIG{CHLD} = \&REAPER; # To reap/eliminate dead children ;  see perlipc
14050   # see man perlfunc, perlfork, perlipc ; Learning Perl 14.6, 3a Ed. ; Perl Cookbook 16.10, 2a. Ed
14051   my $child_pid = fork();
14052   die "fork() failed: $!" unless defined $child_pid;
14053
14054   # convert to local encoding
14055   for (my $i = 0; $i <= $#exec_argv ; $i++){ $exec_argv[$i] = filename_from_unicode $exec_argv[$i]; }
14056
14057   if( $child_pid == 0 ){
14058      #Child process is here ; child process has a zero value
14059      open \*STDIN, '>/dev/null';  # see man perlopentut
14060      open \*STDOUT,'>/dev/null';
14061      open \*STDERR,'>/dev/null';
14062      exec @exec_argv;  # see perlfunc
14063      die "cannot execute $exec_argv[0]: $!\n";
14064   }
14065   #Parent process is here ; parent process has a nonzero value
14066   #print "\n exec_cmd_system2 --> \$child_pid = $child_pid ;; \@exec_argv = @exec_argv\n";
14067   return;
14068}
14069
14070sub exec_cmd_system3 { # used by vorbiscomment and metaflac
14071   my @exec_argv = @_;
14072   my $child_pid = fork();
14073   die "fork() failed: $!" unless defined $child_pid;
14074
14075   if( $child_pid == 0 ){
14076      #Child process is here ; child process has a zero value
14077      open \*STDIN, '>/dev/null';  # see man perlopentut
14078      open \*STDOUT,'>/dev/null';
14079      open \*STDERR,'>/dev/null';
14080      exec @exec_argv;  # see perlfunc
14081      die "cannot execute $exec_argv[0]: $!\n";
14082   }
14083   return;
14084}
14085
14086# change, for exemple, '0,49' to '0.49'.
14087sub number_value{
14088    my $nvalue = shift;
14089    $nvalue =~ s/,/./g;
14090    return $nvalue;
14091}
14092
14093sub print_local {
14094   print filename_from_unicode shift;
14095}
14096
14097#####-----------------------------------------------#####
14098
14099sub minimum {
14100  my $v1 = shift;
14101  my $v2 = shift;
14102  my $min = $v1 < $v2 ? $v1 : $v2 ;
14103  return $min;
14104}
14105
14106sub maximum {
14107  my $v1 = shift;
14108  my $v2 = shift;
14109  my $max = $v1 < $v2 ? $v2 : $v1;
14110  return $max;
14111}
14112
14113
14114# if  ( the extension_input eq extension_output and $check_change_properties is not active
14115# and $check_button_overwrite is not active then make copy.
14116sub same_extension_overwrite {
14117   my %args = ( @_ );
14118   my $filepath  = $args{filepath}; my $file_wav = $args{file_wav};
14119   my $directory = $args{directory};
14120
14121   my @cmd;
14122
14123   #remove wav file if button_del_wav is active and $file_wav exist
14124   if ($check_button_del_wav->get_active and -e filename_from_unicode "$directory_output/$file_wav" ){
14125      @cmd = ( 'rm', '-f', $directory_output.'/'.$file_wav );
14126
14127      insert_msg("@cmd" , "small"); #print on debug textview
14128      insert_msg("\n" , "small");
14129
14130      exec_cmd_system2(@cmd);
14131   }
14132   # if overwrite is active, don't need copy file
14133   if ( not $check_button_overwrite->get_active and (filename_from_unicode $directory ne filename_from_unicode $directory_final) ){
14134
14135      # copy the mp3/mp4/ogg ... files (that don't need to normalize) to $directory_final
14136      @cmd = ( 'cp', '-f', $filepath, $directory_final );
14137
14138      insert_msg("@cmd\n" , "small"); #print on debug textview
14139      insert_msg( $langs{msg056} , "small-red");
14140      insert_msg("\n" , "small");
14141
14142      exec_cmd_system2(@cmd);
14143   }
14144   if ( not $already_normalized ) {
14145      $pbar_n->set_fraction(0) ;
14146      my $sb = $spinner_sensi->get_value;
14147
14148      if ( abs($adjust) <= $sb and $norm_type ne "None" ){
14149
14150         $sb = sprintf("%0.2f", $spinner_sensi->get_value);
14151         $sb = number_value($sb);  # For example, change '0,57' by '0.57'.
14152
14153         $pbar_n->set_text( $langs{msg042} );
14154	 insert_msg( $langs{msg043} , "small");
14155	 insert_msg("|$adjust| <= $sb" , "small-red");
14156	 insert_msg( $langs{msg044} , "small");
14157	 insert_msg("\n-----*****-----" , "small-black");
14158	 insert_msg("\n", "small");
14159      }
14160      else { $pbar_n->set_text( $langs{msg045} );} # for type = None
14161   }
14162   $pbar_encode->set_fraction(0) ;
14163   $pbar_encode->set_text( $langs{msg046} );
14164   $status_bar->push($context_id, " ");
14165}
14166
14167sub finalize_process { #01May2008
14168   my %args = ( @_ );
14169   my $row  = $args{row};
14170   my $file_wav    = $args{file_wav};  my $filepath = $args{filepath}; my $directory = $args{directory};
14171   my $file_input  = $args{filename};
14172   my $file_output = $args{file_output};
14173
14174   my @cmd;
14175   #print "\n finalize_process --> \$file_input = $file_input ;; \$file_output = $file_output ;; \$directory = $directory ;; \$directory_final = $directory_final\n";
14176
14177   #remove wav file_input if button_del_wav is active and $file_wav exist and ...
14178   if ( $check_button_del_wav->get_active and -e filename_from_unicode "$directory_output/$file_wav" and
14179        (( not $button_output_wav->get_active ) or ( $button_output_wav->get_active and $directory_output ne $directory_final ))  ) {
14180
14181      @cmd = ( 'rm', '-f', $directory_output.'/'.$file_wav );
14182
14183      insert_msg( "\n$langs{msg089}\n" , "small-red"); # "Removing temporary file:"
14184      insert_msg("@cmd" , "small");
14185      insert_msg("\n" , "small");
14186
14187      exec_cmd_system2(@cmd);
14188   }
14189   # overwrite: yes or not? That is the question.
14190   # overwrite only if the encoding process have finished, that is, $normalize_button is active
14191   if ( $normalize_button->get_active and $check_button_overwrite->get_active and
14192	-w filename_from_unicode "$directory_final/$file_output" and
14193	((-w filename_from_unicode $directory and $extension_input ne $extension_output) or (-w filename_from_unicode $directory.'/'.$file_input and $extension_input eq $extension_output)) and
14194	$directory_final ne $directory ){
14195
14196      @cmd = ( 'mv', '-f', $directory_final.'/'.$file_output, $directory.'/'.$file_input ) if ($extension_input eq $extension_output);
14197      @cmd = ( 'mv', '-f', $directory_final.'/'.$file_output, $directory )                 if ($extension_input ne $extension_output);
14198
14199      exec_cmd_system2(@cmd); # overwrite
14200      insert_msg("\n$langs{msg005}:", "small-red");
14201      insert_msg("\n@cmd\n", "small");
14202
14203      @cmd = ( 'rmdir', $directory_final );  # 'rmdir' fail if on different mounted file systems.
14204      exec_cmd_system2(@cmd); # remove the directory_final
14205      insert_msg("@cmd", "small");
14206      insert_msg("\n", "small");
14207   }
14208
14209   if ( $normalize_button->get_active and $check_button_overwrite->get_active and
14210	-w filename_from_unicode "$directory_final/$file_output" and
14211	not ((-w filename_from_unicode $directory and $extension_input ne $extension_output) or (-w filename_from_unicode $directory.'/'.$file_input and $extension_input eq $extension_output)) and
14212	$directory_final ne $directory and $extension_input ne 'cda' ){
14213
14214      my $msg;
14215      $msg = "mv: cannot move \"$directory_final/$file_output\" to \"$directory/$file_output\": Permission denied" if ($extension_input eq $extension_output);
14216      $msg = "mv: cannot move \"$directory_final/$file_output\" to \"$directory/\": Permission denied"             if ($extension_input ne $extension_output);
14217
14218      insert_msg("\n$msg", "small-red");
14219      insert_msg("\n", "small");
14220   }
14221
14222   ## set encoding on the final of process
14223   if ( $normalize_button->get_active ) {  # the normalize button is active
14224      insert_msg("-----*****-----\n" , "small-green");
14225      if ($button_output_wav->get_active){ $pbar_encode->set_fraction( 0 ); }
14226      else { $pbar_encode->set_fraction( 1 ); }
14227   }
14228   elsif ( -e filename_from_unicode "$directory_final/$file_output" ) { # if the file exists
14229      #remove normalized file not finished
14230      exec_cmd_system2( 'rm', '-f', $directory_final.'/'.$file_output );
14231   }
14232}
14233
14234
14235# determine the values of 6 encode variables: ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max)
14236# Don't need to determine $quality
14237
14238sub encode_settings {
14239   my %args = ( @_ );
14240   my $row = $args{row}; my $extension_input = $args{extension_input};
14241
14242   # get some technical informations:
14243   my $bitrate_original = $files_info[$row]{bitrate};
14244   my $Frequency = $files_info[$row]{frequency};
14245   my $bitrate_average = $files_info[$row]{bitrate_average};
14246   my $mode_channel = $files_info[$row]{mode};
14247
14248        my ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max);
14249
14250	# button "change properties" active
14251	if ( $check_change_properties->get_active ){
14252	       $mode = $ComboBox_mode->get_active_text;
14253	       $encode = $ComboBox_encode_type->get_active_text;   # refresh value
14254	       if    ( $extension_output eq 'mp3' ) { $bitrate = $spinner_bitrate_mp3->get_value_as_int(); }# for $encode = "average"
14255	       elsif ( $extension_output eq 'mp4' ) { $bitrate = $spinner_bitrate_mp4->get_value_as_int(); }# for $encode = "average"
14256	       elsif ( $extension_output eq 'ogg' ) { $bitrate = $spinner_bitrate_ogg->get_value_as_int(); }# for $encode = "average"
14257	       $freq = number_value($ComboBox_freq->get_active_text);
14258	       # for mp3 constant $bitrate is obtainned from $ComboBox_const_bitrate
14259	       if ( $encode eq "constant" and $extension_output eq 'mp3' ) { $bitrate = $bitrate_constant;}
14260	       $V = $spinner_V->get_value_as_int; # Vmp3
14261	       $Vmp4 = $spinner_Vmp4->get_value;
14262	       $Vogg = $spinner_Vogg->get_value;
14263	       $Vmpc = $spinner_Vmpc->get_value;
14264	}
14265	# button "change properties" not active
14266	# decide if the bitrate of normalized files will be preserved
14267	else {
14268	       $bitrate = $bitrate_original;                          # copy the value of original bitrate
14269	       $freq = $Frequency;
14270	       $encode = $bitrate_average ? "average" : "constant" ;  # only this two possibility when button "change properties" is not active
14271
14272	       if ( $extension_output eq 'mpc' ){
14273	          # estimating a fuction to find $Vmpc from $bitrate: $Vmpc = f($bitrate)
14274	          my $x = number_value($bitrate_original);
14275	          my $adjustm = 0.15*exp(-( ($x-160)^2 )/8000) ;
14276	          $Vmpc = sprintf("%0.2f", ($x/29)*(1 - $adjustm) - 0.2 );
14277		  $Vmpc = number_value($Vmpc);
14278	          #print("bitrate = $x; ajust = $adjustm; Vmpc = $Vmpc \n");
14279	       }
14280	       if ( $extension_input =~ /(wav|cda|ape|flac)/ ){
14281	            $bitrate = 160;
14282		    $freq = 44100;
14283		    $Vogg = $V = 4.0;
14284		    $Vmpc = 5.0;
14285		    $Vmp4 = 200;
14286		    $encode = "average";
14287	       }
14288	       if ( $extension_input eq 'mp3' and defined $mode_channel ){ $mode = $mode_channel; }
14289	       else { $mode = 'joint stereo'; } # $mode is only used by encode_wav_to_mp3()
14290	}
14291        $Vogg = sprintf("%0.1f", $Vogg ); $Vogg = number_value($Vogg); # only one digit
14292	$Vmpc = sprintf("%0.1f", $Vmpc ); $Vmpc = number_value($Vmpc);
14293
14294	$vb_Max = $ComboBox_Max->get_active_text; #refresh value
14295        $vb_Min = $ComboBox_Min->get_active_text;
14296
14297	# for MPEG-2   layer III sample frequencies (kHz):  16  24  22.05
14298        # bitrates (kbps):  8 16 24 32 40 48 56 64 80 96 112 128 144 160
14299	if ( $freq <= 24000 and $extension_output eq 'mp3') {
14300	     $vb_Min = minimum($vb_Min, 160);
14301	     $vb_Max = minimum($vb_Max, 160);
14302	     $bitrate = minimum($bitrate, 160);
14303	}
14304	return ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max);
14305}
14306
14307##-----------------------CDDB--------------------------##
14308# See to <man CDDB_get>
14309# Read the Album, Artist and Track Titles
14310
14311sub get_cddb_info_from_internet { #26Abr2008
14312
14313        my %config;
14314	my $CDDB_server = $entry_CDDB_server->get_text;
14315	my $cddb_port = $spinner_cddb_port->get_value;
14316	my $cddb_transport = $ComboBox_cddb_transport->get_active_text;
14317	my %cd;
14318
14319        # following variables just need to be declared if different from defaults
14320        $config{CDDB_HOST}=$CDDB_server;             # set cddb host ;; default = "freedb.freedb.org"
14321        $config{CDDB_PORT}=$cddb_port;               # set cddb port
14322        $config{CDDB_MODE}=$cddb_transport;          # set cddb mode: cddb or http
14323        $config{CD_DEVICE}="$audiodevice_path";      # set cd device
14324
14325	my $cddbid = $use_external_cddbget ? get_discids($audiodevice_path)->[0] : CDDB_get::Internal::get_discids($audiodevice_path)->[0];
14326        $cddbid = sprintf("%08x", $cddbid);
14327	#print "\n\n get_cddb_info_from_internet --> \$cddbid = $cddbid\n\n ";
14328
14329        # user interaction welcome?
14330        $config{input}=0;   # 1: ask user if more than one possibility
14331                            # 0: no user interaction
14332
14333	my $info_cddb = "";
14334	my $msg;
14335
14336	# ($Title,$Artist,$Album,$Comment,$Year,$Track_Number,$Total_Track,$Genre) ARE NOT Global variables
14337	# all the 8 tags
14338        my $Title   = "";
14339        my $Artist  = "artist";
14340        my $Album   = "album";
14341        my $Genre   = "Other";
14342	my $Comment = "";
14343        my $Year    = "";
14344        my $Track_Number = "";
14345        my $Total_Track  = "";
14346
14347	$info_cddb = get_cddb_info_from_home(); # return the cddb filename
14348	if ( $info_cddb ne "" and not $check_button_overwrite_cddb->get_active ){
14349	   $msg = $langs{msg020} . $info_cddb;
14350	   return ( msg => $msg ); # not overwrite
14351	}
14352	else{
14353	   eval {%cd = $use_external_cddbget ? get_cddb(\%config) : CDDB_get::Internal::get_cddb(\%config);};    # get info from internet
14354	   if($@) {
14355	      if ( $info_cddb ne "" ) { $msg = $langs{msg071} . "$info_cddb."; }
14356	      else { $msg = $langs{msg072}; }  # " cddb: can't connect to internet!";
14357	      return ( msg => $msg );          # not overwrite
14358	   }
14359	}
14360	$Artist = $cd{artist};     $Artist = remove_change_10_chars($Artist);
14361	$Album  = $cd{title};      $Album  = remove_change_10_chars($Album);
14362	$Genre  = $cd{genre};      $Genre  = remove_change_10_chars($Genre);
14363	$Year   = $cd{year};       $Year   = remove_change_10_chars($Year);
14364        $Total_Track = $cd{tno};   $Total_Track  = remove_change_10_chars($Total_Track);
14365
14366	# initial values
14367        if ( $Artist eq "" or not defined $Artist ) { $Artist = "artist"; }
14368        if ( $Album  eq "" or not defined $Album  ) { $Album  = "album";  }
14369        if ( $Genre  eq "" or not defined $Genre  ) { $Genre  = "Other";  }
14370        if ( $Year   eq "" or not defined $Year   ) { $Year   = "";       }
14371
14372	my $model = $treeview_play->get_model;
14373
14374        for (my $row=0; $row<=$Total_Track; $row++){
14375
14376	  my $track = sprintf("%02d", $row+1);
14377          my $titulo = $cd{track}->[$row];   # foreach my $titulo ( @{$cd{track}} ) {
14378	  $titulo = remove_change_10_chars($titulo);
14379
14380          my $iter  = $model->get_iter_from_string ("$row");
14381
14382          # set all the new 8 entries:
14383
14384	  eval { $titulo = filename_to_unicode $titulo; };
14385          $files_info[$row]{title}    = $titulo;
14386
14387	  eval { $Artist = filename_to_unicode $Artist; };
14388          $files_info[$row]{artist}   = $Artist;
14389
14390	  eval { $Album = filename_to_unicode $Album; };
14391          $files_info[$row]{album}    = $Album;
14392
14393	  eval { $Genre = filename_to_unicode $Genre; };
14394          $files_info[$row]{genre}    = $Genre;
14395
14396          #$files_info[$row]{comment} = $Comment;
14397          $files_info[$row]{year}     = $Year;
14398          #$files_info[$row]{track}       = $Track_Number;
14399          #$files_info[$row]{total_track} = $Total_Track;
14400
14401          $model->set ($iter, COLUMN_ARTIST, $files_info[$row]{artist}, # set value on model
14402                              COLUMN_TITLE,  $files_info[$row]{title},
14403		              COLUMN_ALBUM,  $files_info[$row]{album},
14404		             #COLUMN_TRACK,  $files_info[$row]{track},  # $Track_Number,
14405		              COLUMN_YEAR,   $files_info[$row]{year},
14406	              ) if $iter;
14407        }
14408	save_cddb_info_to_home(0); # save info to "$home/.cddb/ ($dir/) $cddbid"
14409}
14410
14411
14412##-----------------------------------------------------##
14413
14414sub save_cddb_info_to_home {	#26jan2008
14415   my $row = shift;
14416   return if ($files_info[$row]{extension_input} ne 'cda');
14417
14418   my $Title   = $files_info[$row]{title};
14419   my $Artist  = $files_info[$row]{artist};
14420   my $Comment = $files_info[$row]{comment};
14421   my $Album   = $files_info[$row]{album};
14422   my $Genre   = $files_info[$row]{genre};
14423   my $Year    = $files_info[$row]{year};
14424   my $Track_Number = $files_info[$row]{track};
14425   my $Total_Track  = $files_info[$row]{total_track};
14426
14427	my $info_cddb = undef; #filename of $info_cddb saved at $home
14428
14429	# Get ID number of CD.
14430	my $cddbid = $use_external_cddbget ? get_discids($audiodevice_path)->[0] : CDDB_get::Internal::get_discids($audiodevice_path)->[0];
14431	$cddbid = sprintf("%08x", $cddbid);
14432
14433	#print "\n\n save_cddb_info_to_home --> \$cddbid = $cddbid\n\n ";
14434
14435	# -d : if file is a directory
14436	if ( -d filename_from_unicode "$home/.cddb" ){
14437	   opendir(DIR, filename_from_unicode "$home/.cddb" ) or die "couldn't execute opendir: $!\n";;  #search for $home/.cddb/$Genre/ directory
14438	      while (my $direct = readdir(DIR)) {
14439	      next if $direct eq ".";
14440	      next if $direct eq "..";
14441	      if ( -e filename_from_unicode "$home/.cddb/$direct/$cddbid" ){
14442	         $info_cddb = "$home/.cddb/$direct/$cddbid";
14443	         last;
14444	      }
14445	   }
14446	   closedir(DIR);
14447	}
14448	# make the directory "$home/.cddb/$Genre" if it not exist
14449	# if the file "$home/.cddb/$direct/$cddbid" don't exist, make the file "$home/.cddb/$Genre/$cddbid".
14450	if ( not defined $info_cddb ){
14451	   my @cmd = ( 'mkdir', '-p', $home.'/.cddb/'.$Genre ); # create the directory .cddb/$Genre
14452           exec_cmd_system2(@cmd);
14453
14454	   $info_cddb = "$home/.cddb/$Genre/$cddbid";
14455	   insert_msg("Saved cddb file to $info_cddb", "small");
14456           insert_msg("\n", "small");
14457	}
14458
14459	my $first_artist = $files_info[0]{artist};
14460	my $DiscArtist   = $first_artist;
14461	my $multi_artist = $false;
14462
14463	for (my $row=0; $row <= $#files_info ; $row++){         # determine if the CD is multi artist
14464	   if ($files_info[$row]{extension_input} ne 'cda'   ){last;}
14465           if ($files_info[$row]{artist}    eq 'artist'){next;}
14466	   if ( uc($first_artist) ne uc($files_info[$row]{artist}) ){$multi_artist = $true;}
14467        }
14468
14469	# See 'man perlopentut' for '+>' to specify both read and write access
14470	open( CDDB, '+>', "$info_cddb" ) or die "Can not write to $info_cddb: $!";
14471	   print CDDB "# created by gnormalize - version $VERSION\n";
14472	   print CDDB "# homepage: $HOMEPAGE\n\n";
14473	   print CDDB "DISCID=$cddbid\n";
14474	   print CDDB "DTITLE=$DiscArtist / $Album\n";
14475	   print CDDB "DYEAR=$Year\n";
14476	   print CDDB "DGENRE=$Genre\n";
14477
14478	   # fill the music titles	and artist name
14479	   for (my $row=0; $row <= $#files_info ; $row++){
14480	      if ($files_info[$row]{extension_input} ne 'cda'){last;}
14481	      print CDDB "TTITLE$row=$files_info[$row]{title}\n";
14482	      print CDDB "TARTIST$row=$files_info[$row]{artist}\n" if $multi_artist;
14483           }
14484	close CDDB;
14485
14486	# some programs search for cddbid at "$home/.cddb/$cddbid", then make a symbolic link.
14487	# symlink OLDFILE,NEWFILE  ;; see perlfunc
14488	my $symlink_exists = eval { symlink( $info_cddb,"$home/.cddb/$cddbid" ); 1 };
14489	if ( $symlink_exists != 1 ){ link $info_cddb,"$home/.cddb/$cddbid"; }
14490
14491	my $model = $treeview_play->get_model;
14492	for (my $row=0; $row<=$#files_info;$row++){
14493	   next if ($files_info[$row]{extension_input} ne 'cda');
14494	   my $iter  = $model->get_iter_from_string ("$row");
14495
14496	   $files_info[$row]{filepath} = make_filepath_format_for_cda($row);
14497	   $files_info[$row]{filename} = make_filename_format_for_cda($row);
14498           $files_info[$row]{directory_remain} = remove_directory_base($audiodevice_path,$files_info[$row]{filepath}); # mar2008
14499
14500	   $model->set ($iter, COLUMN_FILEPATH, $files_info[$row]{filepath},
14501			       COLUMN_FILENAME, $files_info[$row]{filename},
14502	               ) if $iter;
14503	}
14504
14505	my %hash = determine_directory_and_filename_and_extension(row => $row);
14506	set_output_and_refresh_progress_bar(%hash);
14507
14508	show_scrolling_text(); # Display the "Artist - Title - Album ..."
14509	make_artist_and_album_treeview_column( make_artist_column => $true, make_album_column => $true, array_with_tracks => \@files_info );
14510
14511	#print "\n save_cddb_info_to_home --> \$DiscArtist = $DiscArtist ;; \$Album = $Album\n";
14512
14513	return $info_cddb; # the filename
14514}
14515
14516##-----------------------------------------------------##
14517
14518# Get cddb info and return the cddb filename
14519sub get_cddb_info_from_home {
14520        my $model = $treeview_play->get_model;
14521	my $info_cddb = ""; #filename
14522
14523        # Get ID number and track number of CD.
14524	my $cddbid = $use_external_cddbget ? get_discids($audiodevice_path)->[0] : CDDB_get::Internal::get_discids($audiodevice_path)->[0];
14525	$cddbid = sprintf("%08x", $cddbid);
14526	my $tracks = $use_external_cddbget ? get_discids($audiodevice_path)->[1] : CDDB_get::Internal::get_discids($audiodevice_path)->[1];
14527	#print "cddbid = $cddbid ;; tracks = $tracks ;; home - $home \n";
14528
14529	# -d : if file is a directory
14530	if ( -d "$home/.cddb" ){
14531	   opendir(DIR, "$home/.cddb" ) or die "couldn't execute opendir: $!\n";  #search for $genre/$cddbid file
14532	   while (my $dir = readdir(DIR)) {
14533		next if $dir eq ".";
14534		next if $dir eq "..";
14535		if ( -e filename_from_unicode "$home/.cddb/$dir/$cddbid" ){
14536		   $info_cddb = "$home/.cddb/$dir/$cddbid";
14537		   #print "-->dir = $dir ;; info_cddb = $info_cddb\n";
14538		   last;
14539		}
14540		#print "dir = $dir ;; info_cddb = $info_cddb\n";
14541	   }
14542	   closedir(DIR);
14543	}
14544
14545	# the file "$home/.cddb/$cddbid" have less preference than "$home/.cddb/$dir/$cddbid"
14546	if ( -e filename_from_unicode "$home/.cddb/$cddbid" and $info_cddb eq "" ){$info_cddb = "$home/.cddb/$cddbid";}
14547	if ( $info_cddb eq "" ){ return $info_cddb;} # no file found
14548	#print "\$info_cddb = $info_cddb\n";
14549
14550	#input:
14551	#DISCID=29107b14
14552        #DTITLE=Alceu Valenca / Focus
14553        #DYEAR=1999
14554        #DGENRE=unknown
14555        #DID3=12
14556        #TTITLE0=Estacao da luz
14557	#TARTIST0=Claudio    # if have multi artist
14558        #TTITLE1=petalas
14559	#TARTIST1=Alceu
14560        #TTITLE2=leque moleque
14561	#TARTIST2=Jo�o
14562
14563	# ($Title,$Artist,$Album,$Comment,$Year,$Track_Number,$Total_Track,$Genre) ARE NOT Global variables
14564	# all the 8 tags
14565        my $Title   = "";
14566        my $Artist  = "artist";
14567        my $Album   = "album";
14568        my $Genre   = "Other";
14569	my $Comment = "";
14570        my $Year    = "";
14571        my $Track_Number = "";
14572        my $Total_Track  = "";
14573
14574	my @ttitle; my @tartist;
14575
14576	## 26jan2008
14577	open( INFOCDDA, "<", $info_cddb ) or die "Can't open <$info_cddb>: $!, stopped";     # read from file
14578	while( <INFOCDDA> )                                # Get Artist/Album/Year/Genre/title info.
14579	{
14580	     if ($_ =~ /DTITLE=\s*(.*)\s*\/\s*(.*)\s*\n/gi){
14581	        $Artist = remove_whitespace_character($1);
14582	        $Album  = remove_whitespace_character($2); }
14583	     if ($_ =~ /DYEAR=(\d{4})\n/i){ $Year = $1; }
14584	     if ($_ =~ /DGENRE=(.*)\n/i){ $Genre = $1; }
14585	     if ($_ =~ /TITLE(\d+)=(.*)\n/i){ $ttitle[$1] = remove_whitespace_character($2); }
14586	     if ($_ =~ /ARTIST(\d+)=(.*)\n/i){ $tartist[$1] = remove_whitespace_character($2); } #multi artist
14587	}
14588	close(INFOCDDA);
14589
14590	# initial values
14591	if ( $Artist eq "" or not defined $Artist ) { $Artist = "artist"; }
14592	if ( $Album  eq "" or not defined $Album  ) { $Album  = "album";  }
14593	if ( $Genre  eq "" or not defined $Genre  ) { $Genre  = "Other";  }
14594	if ( $Year   eq "" or not defined $Year   ) { $Year   = "";       }
14595
14596	$Artist = remove_change_10_chars($Artist);
14597	$Album  = remove_change_10_chars($Album);
14598	$Genre  = remove_change_10_chars($Genre);
14599	$Year   = remove_change_10_chars($Year);
14600
14601	eval { $Album = filename_to_unicode $Album; };
14602	eval { $Genre = filename_to_unicode $Genre; };
14603
14604	for (my $row=0; $row<=$#files_info;$row++){
14605
14606	   next if ($files_info[$row]{extension_input} ne 'cda');
14607	   my $iter  = $model->get_iter_from_string ("$row");
14608
14609	   $files_info[$row]{comment}     = "";
14610	   $files_info[$row]{genre}       = $Genre;
14611	   $files_info[$row]{total_track} = $#files_info + 1; # $Total_Track
14612
14613	   my $art = defined $tartist[$row] ? $tartist[$row] : $Artist;
14614	   $art    = remove_change_10_chars($art);
14615	   eval { $art = filename_to_unicode $art; };
14616	   $files_info[$row]{artist} = $art;
14617           $model->set ($iter, COLUMN_ARTIST, $files_info[$row]{artist});
14618
14619	   if (	defined $ttitle[$row] ){
14620	      $ttitle[$row] = remove_change_10_chars( $ttitle[$row] );
14621	      eval { $ttitle[$row] = filename_to_unicode $ttitle[$row]; };
14622	      $files_info[$row]{title} = $ttitle[$row];
14623              $model->set ($iter, COLUMN_TITLE, $files_info[$row]{title});
14624	   }
14625
14626	   $files_info[$row]{album} = $Album;
14627           $model->set ($iter, COLUMN_ALBUM, $files_info[$row]{album});
14628
14629	   $files_info[$row]{year} = $Year;
14630           $model->set ($iter, COLUMN_YEAR, $files_info[$row]{year});
14631	}
14632	for (my $row=0; $row<=$#files_info;$row++){
14633	   if ($files_info[$row]{extension_input} ne 'cda'){next;}
14634
14635	   my $iter  = $model->get_iter_from_string ("$row");
14636
14637	   $files_info[$row]{filepath} = make_filepath_format_for_cda($row);
14638	   $files_info[$row]{filename} = make_filename_format_for_cda($row);
14639           $files_info[$row]{directory_remain} = remove_directory_base($audiodevice_path,$files_info[$row]{filepath}); # mar2008
14640
14641	   $model->set ($iter, COLUMN_FILEPATH, $files_info[$row]{filepath},
14642			       COLUMN_FILENAME, $files_info[$row]{filename},
14643	               ) if $iter;
14644	}
14645	return $info_cddb;
14646}
14647
14648sub remove_whitespace_character {
14649   my $char = shift;
14650   $char =~ s/^\s*//;  # remove the \s whitespace character at the first position.
14651   $char =~ s/\s*$//;  # remove the \s whitespace character at the end position.
14652   $char =~ s/\s+/ /g; # replace more than one \s whitespace character by only one.
14653   return $char;
14654}
14655
14656##-----------------------------------------------------##
14657
14658#  cda --> wav   ; rip audio cd with cdparanoia
14659
14660sub refresh_cda {
14661        $audiodevice_path = $entry_cda->get_text; # /dev/hdc or /dev/cdrom or ...
14662        stop_playing_music();
14663
14664	# make 3 arrays with frames of audio cd and return $total_frames number.
14665	# The 3 arrays are @length, @music_sector and @start_sector.
14666	my %hash = length_music_sector();
14667	my $total_frames = $hash{total_frames} if $hash{total_frames};
14668	my $msg = $hash{msg} if $hash{msg};
14669
14670	print filename_from_unicode "\n\n ---> \$msg = $msg\n\n" if $msg;
14671	#print "\n\$ ---> total_frames = $total_frames\n\n" if $total_frames;
14672
14673	# get the old model from Gtk2::TreeView
14674        # clear the old model - see <man Gtk2::ListStore>
14675	$treeview_play->get_model->clear;
14676	$treeview_album->get_model->clear;
14677        $treeview_artist->get_model->clear;
14678
14679	#print "\n refresh_cda ---> aqui\n";
14680
14681	# create the new tree model
14682        my $model = create_model_audio_cd(@length); # and fill the @files_info
14683
14684	# refresh tree view
14685        $treeview_play->set_model($model);
14686	count_artists_and_album_already_played(update_model_artist => $false, update_model_album => $false);
14687
14688        # add columns to the tree view
14689        add_columns(show_file => $false, show_play => $false);
14690
14691	if ( defined $files_info[0]{filepath} and $total_frames > 0 ){ # exist, at least, one audiocd track
14692	   $force_unselect_all_tracks = $false; # to select all tracks
14693	   unselect_cda();  # subroutine that select all tracks
14694	   $button_unselec->set_sensitive($true);
14695	   $button_selec->set_sensitive($true);
14696	   if ( defined $cdcd_path or $use_audiocd eq $true or $os =~ /Linux/i ){
14697	      $da->set_sensitive($true);
14698	      @rows_already_played = ();
14699	      $go_forward = $false;
14700	      $go_back = $false;
14701	      $have_current_track = audio_cd_have_current_track($audiodevice_path)
14702	                            if ($use_audiocd and $cdplayer eq "Audio::CD");
14703	   }
14704	   $normalize_button->set_label("rip + normalize + encode");
14705	   change_font_for_all_child($window_font_name,$normalize_button);
14706
14707	   $button_save_tag->set_sensitive($false);
14708
14709	   my %hash_cddb;
14710
14711	   if ( $check_button_cddb->get_active ){
14712	      $status_bar->push($context_id, $langs{msg068} ); # " Getting information from cddb ...";
14713	      while (Gtk2->events_pending()) { Gtk2->main_iteration()};
14714	      %hash_cddb = get_cddb_info_from_internet();
14715	      $msg = $hash_cddb{msg};
14716	   }
14717	   else { $msg = $langs{msg069}; }
14718
14719	   get_selection_tree(); # select the first file and get info from file
14720
14721	   print_cdparanoia_output($total_frames);
14722	   show_scrolling_text(); # Display the "Artist - Title - Album ..."
14723	}
14724	else { entry_cda_change(); }
14725
14726	make_artist_and_album_treeview_column( make_artist_column => $true, make_album_column => $true, array_with_tracks => \@files_info );
14727
14728	# verify if some ripper is available.
14729	if ( $ripper eq "cdparanoia" and not $cdparanoia_path ){
14730           $status_bar->push($context_id, $langs{msg073} );
14731           $extension_input = 'error';
14732           return $false;
14733        }
14734	elsif ( $ripper eq "cdda2wav" and not $cdda2wav_path ){
14735           $status_bar->push($context_id, $langs{msg074} );
14736           $extension_input = 'error';
14737           return $false;
14738        }
14739	# show final msg
14740	$status_bar->push($context_id, $msg ) if $msg;
14741}
14742
14743sub entry_cda_change {
14744   $button_selec->set_sensitive($false);
14745   $button_unselec->set_sensitive($false);
14746   $da->set_sensitive($false);
14747   $extension_input = 'error'; # rip audio cd is not possible
14748   stop_playing_music();
14749   #if ( defined($timer) ){ Glib::Source->remove($timer);} # quit loop, see <man Glib::MainLoop>
14750
14751   # erase all info
14752
14753   # clear the old model - see <man Gtk2::ListStore>
14754   $treeview_play->get_model->clear;
14755   $treeview_album->get_model->clear;
14756   $treeview_artist->get_model->clear;
14757
14758   my $row = 0;
14759
14760   # erase all technical informations:
14761   $files_info[$row]{extension_input} = 'mp3';
14762   $files_info[$row]{technical_info}  = "MPEG";  # standard value
14763   $files_info[$row]{bitrate_original}  = 160;   # standard value if "change properties" is not active
14764   $files_info[$row]{bitrate_average}  = $true;  # $true or $false
14765   $files_info[$row]{mode} = '';
14766   $files_info[$row]{length} = '';
14767   $files_info[$row]{bitrate} = undef;
14768   $files_info[$row]{frequency} = undef;
14769   $files_info[$row]{fileSize} = undef;
14770
14771   # erase all the 8 tags
14772   $files_info[$row]{title}  = "";
14773   $files_info[$row]{artist}  = "";
14774   $files_info[$row]{album}  = "";
14775   $files_info[$row]{track}  = ""; # track = track_number
14776   $files_info[$row]{total_track}  = "";
14777   $files_info[$row]{comment}  = "";
14778   $files_info[$row]{year}  = "";
14779   $files_info[$row]{genre}  = "";
14780
14781   set_tag_info($row);
14782
14783   # erase progress bar names
14784   $pbar->set_text(" ");          $pbar->set_fraction(0);
14785   $pbar_n->set_text(" ");        $pbar_n->set_fraction(0);
14786   $pbar_encode->set_text(" ");   $pbar_encode->set_fraction(0);
14787
14788   my %hash = determine_directory_and_filename_and_extension(row => $row);
14789   refresh_input_output_arrows();
14790   $status_bar->push($context_id, "" );
14791
14792   $normalize_button->set_label("normalize");
14793
14794   @files_info = ();              # reset the array
14795   %albums_already_played = ();   # reset the hash whose key is only the played album name
14796   %artists_already_played = ();  # reset the hash whose key is only the played artist name
14797   @length = ();         # array of music length
14798   @music_sector = ();   # array of relative frames
14799   @start_sector = ();   # absolute frames - 150 (offset)
14800   $selected_row = 0;
14801}
14802
14803sub length_music_sector { # make this 3 arrays
14804   @length = ();       # array of music length
14805   @music_sector = (); # array of relative frames
14806   @start_sector = (); # absolute frames - 150
14807   my $total_frames = 0;
14808   my $msg;
14809   my $channel;
14810
14811   if ($os =~ /Linux/i) {
14812	$toc_audiocd = gnormalize::cdplay::read_toc($cdplayer); # toc of audiocd, array of reference
14813        if ( not $toc_audiocd ){ # can't read the cdrom drive
14814	   $button_selec->set_sensitive($false);
14815           $da->set_sensitive($false);
14816	   $msg = $langs{msg067}."$audiodevice_path.";
14817	   return ( total_frames => $total_frames, msg => $msg );
14818	}
14819        my $total_track = $toc_audiocd->[0]->{total_track};
14820	$channel = 2;
14821        for (my $i = 1; $i <= $total_track; $i++) {
14822           my $min = $toc_audiocd->[$i]->{min};
14823           my $sec = $toc_audiocd->[$i]->{sec};
14824           push @length ,"$min:$sec" ;
14825           push @music_sector ,$toc_audiocd->[$i]->{frames};       # for relative frames
14826           push @start_sector ,$toc_audiocd->[$i]->{begin_frames}; # for absolute frames - 150 (gap)
14827        }
14828	$total_frames = $toc_audiocd->[0]->{total_frames};
14829   }
14830   elsif ( defined $cdparanoia_path ){ # use cdparanoia for other operational system
14831
14832        #my $cdparanoia_path = '/usr/bin/cdparanoia';
14833
14834	# cdda2wav  dev=/dev/hdc  -J -v toc
14835	# $cdda2wav_path  dev=$audiodevice_path  -J -v toc
14836
14837	$status_bar->push($context_id, $langs{msg021} );
14838	while (Gtk2->events_pending()) { Gtk2->main_iteration()};
14839
14840	$priority = $spinner_pri->get_value;
14841
14842	# -Q --query  Perform  CDROM  drive autosense, query and print the CDROM table
14843        # of contents, then quit.
14844
14845	my $cmd = "$nice_path -n $priority $cdparanoia_path  -Q -d $audiodevice_path ";
14846
14847	my $list = exec_cmd_system($cmd);
14848
14849	if ( $list =~ /Unable\sto\sopen\sdisc/is or $list =~ /Unable\sto\sopen\scdrom\sdrive/is ){
14850	   $button_selec->set_sensitive($false);
14851           $da->set_sensitive($false);
14852	   $msg = $langs{msg067}."$audiodevice_path.";
14853	   return ( total_frames => $total_frames, msg => $msg );
14854	}
14855	#output:
14856	# Table of contents (audio tracks only):
14857        #track        length               begin        copy pre ch
14858        #===========================================================
14859        #  1.    11900 [02:38.50]        0 [00:00.00]    no   no  2
14860        #  2.    21945 [04:52.45]    11900 [02:38.50]    no   no  2
14861	#  3.    18505 [04:06.55]    33845 [07:31.20]    no   no  2
14862	# 18.    15580 [03:27.55]   259607 [57:41.32]    no   no  2
14863	# <man perlrequick or man perlretut>
14864
14865	# @length is an array with length of the songs
14866	while( $list =~ /\s*\d+\.\s+(\d+)\s+\[(.*):(\d+\.\d+)\]\s+(\d+)\s+\[.*\]\s+\w+\s+\w+\s+(\d+)/g )
14867	{
14868	   my $sec = number_value(sprintf("%02d", $3 )); # 02d: leading zero
14869	   push @length ,"$2:$sec" ;
14870	   push @music_sector , $1;
14871	   push @start_sector , $4;
14872	   $channel = $5;
14873	}
14874	# Audio CD Total Time
14875	# 23.    18012 [04:00.12]   322529 [71:40.29]    no   no  2
14876        #TOTAL  340541 [75:40.41]    (audio only)
14877	while( $list =~ /TOTAL\s+(\d+)\s+\[(\d+):(\d+\.\d+)\]\s+/g ) {
14878	   $total_frames = $1;
14879	}
14880   } # cdparanoia , final
14881
14882   # Get the $fileSize
14883   my $totalbytes = $total_frames * 2352 ;
14884   push @start_sector, $total_frames;
14885   $audio_cd_fileSize = sprintf("%.1f", $totalbytes/(1024*1024) );
14886   $audio_cd_channel = $channel;
14887
14888   # Audio CD size
14889   # see /usr/include/linux/cdrom.h
14890   # CD_FRAMES            75 /* frames per second */
14891   # CD_FRAMESIZE_RAW   2352 /* bytes per frame, "raw" mode */
14892   # 1 second have 75 frames, that have 75*2352 bytes = 75*2352*8 bits = 1 411 200 bits = 1411.2 kbits
14893   # So, the bitrate of an audio cd is equal to 1411.2 kb/s .
14894
14895   my $minTot = $total_frames / (60 * 75);
14896   my $secTot = ($total_frames / 75) % 60;
14897   $minTot = sprintf ( "%02d", $minTot);
14898   $secTot = sprintf ( "%02d", $secTot);
14899   $audio_cd_total_time = "$minTot:$secTot";
14900   $audio_cd_total_time = time_to_sec($audio_cd_total_time);
14901
14902   # convert x seconds to hour:min:sec format
14903   my ($hourT,$minT,$secT) = sec_to_time($audio_cd_total_time);    # return ($hour,$min,$sec);
14904
14905   $audio_cd_total_time = $hourT > 0 ? "$hourT:$minT:$secT" : "$minT:$secT" ;
14906
14907   #for (my $i=0;$i<@length;$i++){ print "i=$i ; length=$length[$i] ; $music_sector[$i] ; $start_sector[$i] \n"; }
14908   return ( total_frames => $total_frames, msg => $msg );
14909}
14910
14911$buffer->create_tag ("paranoia",     'foreground' => 'blue',    # see <man Gtk2::TextTag> for more options
14912                                     'background' => '#B8D3C1', # #B8D3C1 = 184 211 193 (RGB)
14913                                  'justification' => 'center',
14914                                      left_margin => 0,
14915				            style => 'normal',
14916				        wrap_mode => 'word',
14917					    scale => '1.1',
14918					   family => 'Courier',
14919					  # family => 'Terminal',
14920				          # family => 'Fixed',
14921                                    'size-points' => '11');
14922
14923sub print_cdparanoia_output {
14924   my $total_frames = shift || 0;
14925
14926   my $art = $files_info[0]{artist}; # [0] is the first artist
14927   my $alb = $files_info[0]{album};
14928
14929   insert_msg("\n$art / $alb" , "small-black");
14930
14931   insert_msg("\nTable of contents (audio tracks only):      \n" , "paranoia");
14932   insert_msg("track        length               begin     \n" , "paranoia");
14933   insert_msg("============================================\n" , "paranoia");
14934
14935   for (my $track = 1; $track <= @music_sector; $track++) {
14936      my $frames = $music_sector[$track-1];       # for relative frames
14937      my $begin_frames = $start_sector[$track-1]; # for absolute frames - 150 (gap)
14938
14939      my $minf = sprintf ( "%02d", $frames / (60 * 75) );
14940      my $secf = sprintf ( "%02d", ($frames / 75) % 60 );
14941      my $fraf = sprintf ( "%02d", $frames % 75 );
14942
14943      my $minb = sprintf ( "%02d", $begin_frames / (60 * 75) );
14944      my $secb = sprintf ( "%02d", ($begin_frames / 75) % 60 );
14945      my $frab = sprintf ( "%02d", $begin_frames % 75 );
14946
14947      # $track = " " x ( 3 - length($track) ) . $track; # the same result
14948      $track  = sprintf ("%3s", $track  );  # s: spaces, see perlfunc.
14949      $frames = sprintf ("%7s", $frames );
14950      $begin_frames = sprintf ("%7s", $begin_frames );
14951
14952      insert_msg("$track.  $frames [$minf:$secf.$fraf]  $begin_frames [$minb:$secb.$frab]\n" , "paranoia");
14953   }
14954   my $mint = sprintf ( "%02d", $total_frames / (60 * 75) );
14955   my $sect = sprintf ( "%02d", ($total_frames / 75) % 60 );
14956   my $frat = sprintf ( "%02d", $total_frames % 75 );
14957   $total_frames = sprintf ("%7s", $total_frames );
14958
14959   insert_msg("TOTAL $total_frames [$mint:$sect.$frat]    (audio only)    \n", "paranoia");
14960
14961   # like 'cdparanoia -Q'
14962   #printf "%3d.  %7ld [%02d:%02d.%02d]  %7ld [%02d:%02d.%02d]\n", $track,
14963   #     $frames, ($frames / (60 * 75)), (($frames/75)%60), ($frames % 75),
14964   #     $begin_frames, ($begin_frames/(60*75)), (($begin_frames/75)%60), ($begin_frames % 75);
14965}
14966
14967sub add_columns {
14968   my %args = ( show_filepath  => $column_show_filepath,
14969                show_filename  => $column_show_filename,
14970                show_rip       => $false,
14971                show_play      => $false,
14972		show_file      => $column_show_file,
14973		show_artist    => $column_show_artist,
14974		show_album     => $column_show_album,
14975		show_track     => $column_show_track,
14976		show_bitrate   => $column_show_bitrate,
14977		show_year      => $column_show_year,
14978		show_frequency => $column_show_frequency,
14979		show_extension => $column_show_extension,
14980		treeview       => $treeview_play,
14981                @_,             # argument pair list goes here
14982	      );
14983  my $treeview = $args{treeview};
14984  my @all_columns = $treeview->get_columns; # Retuns an array of columns.
14985  foreach my $col (@all_columns){ $treeview->remove_column($col); }
14986
14987
14988  # column for fixed toggles
14989  my $renderer_rip = Gtk2::CellRendererToggle->new;
14990  $renderer_rip->signal_connect (toggled => \&fixed_toggled);
14991  $renderer_rip->set_data (column => COLUMN_RIP);
14992
14993  my $column_rip = Gtk2::TreeViewColumn->new_with_attributes ("rip", $renderer_rip, active => COLUMN_RIP,
14994                                                              'cell-background-gdk' => COLUMN_COLOR);
14995  # set this column to a fixed sizing (of 40 pixels)  ;
14996  # $column_rip->set_sizing ('fixed'); $column_rip->set_fixed_width (40);
14997  $column_rip->set_resizable ($true);
14998  $column_rip->set_min_width (34);
14999  $column_rip->set (alignment => 0.5, 'clickable' => $true);  # 'clickable' : Whether the header can be clicked
15000  $column_rip->signal_connect ( clicked => \&unselect_cda);
15001
15002  my $all_track_is_cda = $true;
15003  for (my $row = 0; $row <= $#files_info ; $row++){
15004     last if (not defined $files_info[0]{extension_input});
15005     if ($files_info[$row]{extension_input} ne 'cda' ){ $all_track_is_cda = $false; last; };
15006  };
15007  $column_rip->set_title( $all_track_is_cda ? "rip" : $langs{name065} );
15008  $treeview->insert_column ($column_rip, COLUMN_RIP);
15009
15010
15011  if ( $args{show_file} ){
15012     # file column
15013     my $renderer_file = Gtk2::CellRendererText->new;
15014     $renderer_file->set ('font' => $window_font_name, 'xalign' => 0.5);
15015     $renderer_file->set_data (column => COLUMN_FILE);
15016
15017     my $column_file = Gtk2::TreeViewColumn->new_with_attributes ($langs{name007}, $renderer_file, text => COLUMN_FILE,
15018                                                                  'cell-background-gdk' => COLUMN_COLOR);
15019     $column_file->set_min_width (40);
15020     $column_file->set_resizable ($true);
15021     $column_file->set (alignment => 0.5); # Alignment of the column header text
15022     $column_file->set_sort_column_id (COLUMN_FILE);
15023     $treeview->insert_column ($column_file, COLUMN_FILE);
15024  }
15025
15026  if ( $args{show_artist} ){
15027     # artist column
15028     $renderer_artist = Gtk2::CellRendererText->new;
15029     #$renderer_artist->signal_connect (edited => \&cell_edited);
15030     $renderer_artist->set_data (column => COLUMN_ARTIST);
15031     $renderer_artist->set ('font' => $window_font_name, style => 'italic', scale => '0.9');
15032
15033     my $column_artist = Gtk2::TreeViewColumn->new_with_attributes ($langs{name046}, $renderer_artist, text => COLUMN_ARTIST,
15034                                                                    #editable => $true,
15035								    'cell-background-gdk' => COLUMN_COLOR);
15036     $column_artist->set_resizable ($true);
15037     $column_artist->set_expand ($true);     # this column expand to max width
15038     #$column_artist->set_sizing ('fixed'); $column_artist->set_fixed_width (300);
15039     $column_artist->set (alignment => 0.0); # Alignment of the column header text
15040     $column_artist->set_sort_column_id (COLUMN_ARTIST);
15041     $treeview->insert_column ($column_artist, COLUMN_ARTIST);
15042  }
15043
15044  # title column
15045  $renderer_title = Gtk2::CellRendererText->new;
15046  #$renderer_title->signal_connect (edited => \&cell_edited);
15047  $renderer_title->set_data (column => COLUMN_TITLE);
15048  $renderer_title->set ('font' => $window_font_name, style => 'italic', scale => '0.9');
15049
15050  my $column_title = Gtk2::TreeViewColumn->new_with_attributes ($langs{name047}, $renderer_title, text => COLUMN_TITLE,
15051                                                                 #editable => $true ,
15052								'cell-background-gdk' => COLUMN_COLOR);
15053  $column_title->set_resizable ($true);
15054  #$column_title->set_min_width (220);
15055  $column_title->set (alignment => 0.0);
15056  $column_title->set_expand ($true);  # this column expand to max width
15057  #$column_title->set_sizing ('fixed'); $column_title->set_fixed_width (300);
15058  $column_title->set_sort_column_id (COLUMN_TITLE);
15059  $treeview->insert_column ($column_title, COLUMN_TITLE);
15060
15061  if ( $args{show_album} ){
15062     # album column
15063     $renderer_album = Gtk2::CellRendererText->new;
15064     $renderer_album->set_data (column => COLUMN_ALBUM);
15065     $renderer_album->set ('font' => $window_font_name, style => 'italic', scale => '0.9');
15066
15067     my $column_album = Gtk2::TreeViewColumn->new_with_attributes ($langs{name053}, $renderer_album, text => COLUMN_ALBUM,
15068                                                                   'cell-background-gdk' => COLUMN_COLOR);
15069     $column_album->set_resizable ($true);
15070     #$column_album->set_min_width (100);
15071     $column_album->set (alignment => 0.0); # Alignment of the column header text
15072     $column_album->set_expand ($true);  # this column expand to max width
15073     $column_album->set_sort_column_id (COLUMN_ALBUM);
15074     $treeview->insert_column ($column_album, COLUMN_ALBUM);
15075  }
15076
15077  if ( $args{show_track} ){
15078     # column for tracks
15079     $renderer_track = Gtk2::CellRendererText->new;
15080     $renderer_track->set ('font' => $window_font_name, 'xalign' => 0.5);
15081     $renderer_track->set_data (column => COLUMN_TRACK);
15082
15083     my $column_track = Gtk2::TreeViewColumn->new_with_attributes ($langs{name045}, $renderer_track, text => COLUMN_TRACK,
15084                                                                   'cell-background-gdk' => COLUMN_COLOR);
15085     $column_track->set_min_width (40);
15086     $column_track->set_resizable ($true);
15087     $column_track->set (alignment => 0.5); # Alignment of the column header text
15088     $column_track->set_sort_column_id (COLUMN_TRACK);
15089     #$column_track->set_expand ($true);
15090     $treeview->insert_column ($column_track, COLUMN_TRACK);
15091  }
15092
15093  # length column
15094  $renderer_length = Gtk2::CellRendererText->new;
15095  $renderer_length->set ('font' => $window_font_name, 'xalign' => 0.5);
15096  my $column_length = Gtk2::TreeViewColumn->new_with_attributes ($langs{name044}, $renderer_length, text => COLUMN_LENGTH,
15097                                                              'cell-background-gdk' => COLUMN_COLOR);
15098  $column_length->set_min_width (50);
15099  $column_length->set_resizable ($true);
15100  $column_length->set (alignment => 0.5); # Alignment of the column header text
15101  $column_length->set_sort_column_id (COLUMN_LENGTH);
15102  $treeview->insert_column ($column_length, COLUMN_LENGTH);
15103
15104  if ( $args{show_bitrate} ){
15105     # bitrate column
15106     my $renderer_bitrate = Gtk2::CellRendererText->new;
15107     $renderer_bitrate->set_data (column => COLUMN_BITRATE);
15108     $renderer_bitrate->set ('font' => $window_font_name, 'xalign' => 0.5);
15109
15110     my $column_bitrate = Gtk2::TreeViewColumn->new_with_attributes ("Bitrate", $renderer_bitrate, text => COLUMN_BITRATE,
15111                                                                   'cell-background-gdk' => COLUMN_COLOR);
15112     $column_bitrate->set_min_width (50);
15113     $column_bitrate->set_resizable ($true);
15114     $column_bitrate->set_expand ($false);
15115     $column_bitrate->set (alignment => 0.5); # Alignment of the column header text
15116     $column_bitrate->set_sort_column_id (COLUMN_BITRATE);
15117     $treeview->insert_column ($column_bitrate, COLUMN_BITRATE);
15118  }
15119  if ( $args{show_year} ){
15120     my $renderer_year = Gtk2::CellRendererText->new;
15121     $renderer_year->set_data (column => COLUMN_YEAR);
15122     $renderer_year->set ('font' => $window_font_name, 'xalign' => 0.5);
15123
15124     my $column_year = Gtk2::TreeViewColumn->new_with_attributes ($langs{name049}, $renderer_year, text => COLUMN_YEAR,
15125                                                               'cell-background-gdk' => COLUMN_COLOR);
15126     $column_year->set_resizable ($true);
15127     $column_year->set (alignment => 0.5); # Alignment of the column header text
15128     $column_year->set_min_width (50);
15129     $column_year->set_sort_column_id (COLUMN_YEAR);
15130     $treeview->insert_column ($column_year, COLUMN_YEAR);
15131  }
15132  if ( $args{show_frequency} ){
15133     my $renderer_frequency = Gtk2::CellRendererText->new;
15134     $renderer_frequency->set_data (column => COLUMN_FREQUENCY);
15135     $renderer_frequency->set ('font' => $window_font_name, 'xalign' => 0.5);
15136
15137     my $column_frequency = Gtk2::TreeViewColumn->new_with_attributes ($langs{name041}, $renderer_frequency, text => COLUMN_FREQUENCY,
15138                                                                     'cell-background-gdk' => COLUMN_COLOR);
15139     $column_frequency->set_resizable ($true);
15140     $column_frequency->set (alignment => 0.5); # Alignment of the column header text
15141     $column_frequency->set_min_width (50);
15142     $column_frequency->set_sort_column_id (COLUMN_FREQUENCY);
15143     $treeview->insert_column ($column_frequency, COLUMN_FREQUENCY);
15144  }
15145  if ( $args{show_filepath} ){
15146     # filename column
15147     my $renderer_filepath = Gtk2::CellRendererText->new;
15148     $renderer_filepath->set_data (column => COLUMN_FILEPATH);
15149     $renderer_filepath->set ('font' => $window_font_name, scale => '0.9');
15150
15151     my $column_filepath = Gtk2::TreeViewColumn->new_with_attributes ($langs{name050}, $renderer_filepath, text => COLUMN_FILEPATH,
15152                                                                   'cell-background-gdk' => COLUMN_COLOR);
15153     $column_filepath->set_resizable ($true);
15154     $column_filepath->set (alignment => 0.0); # Alignment of the column header text
15155     $column_filepath->set_expand ($false);     # this column expand to max width
15156     $column_filepath->set_sort_column_id (COLUMN_FILEPATH);
15157     $treeview->insert_column ($column_filepath, COLUMN_FILEPATH);
15158  }
15159  if ( $args{show_filename} ){
15160     my $renderer_filename = Gtk2::CellRendererText->new;
15161     $renderer_filename->set_data (column => COLUMN_FILENAME);
15162     $renderer_filename->set ('font' => $window_font_name, scale => '0.9');
15163
15164     my $column_filename = Gtk2::TreeViewColumn->new_with_attributes ($langs{name056}, $renderer_filename, text => COLUMN_FILENAME,
15165                                                                   'cell-background-gdk' => COLUMN_COLOR);
15166     $column_filename->set_resizable ($true);
15167     $column_filename->set (alignment => 0.0); # Alignment of the column header text
15168     $column_filename->set_expand ($false);    # this column expand to max width
15169     $column_filename->set_sort_column_id (COLUMN_FILENAME);
15170     $treeview->insert_column ($column_filename, COLUMN_FILENAME);
15171  }
15172  if ( $args{show_extension} ){
15173     # extension column
15174     my $renderer_extension = Gtk2::CellRendererText->new;
15175     $renderer_extension->set_data (column => COLUMN_EXTENSION);
15176     $renderer_extension->set ('font' => $window_font_name, 'xalign' => '0.5');
15177
15178     my $column_extension = Gtk2::TreeViewColumn->new_with_attributes ($langs{name051}, $renderer_extension, text => COLUMN_EXTENSION,
15179                                                                    'cell-background-gdk' => COLUMN_COLOR);
15180     $column_extension->set_resizable ($true);
15181     $column_extension->set_min_width (40);
15182     $column_extension->set (alignment => 0.5); # Alignment of the column header text
15183     $column_extension->set_sort_column_id (COLUMN_EXTENSION);
15184     $treeview->insert_column ($column_extension, COLUMN_EXTENSION);
15185  }
15186}
15187
15188sub add_columns_album_artist { #08Jun2008
15189   my %args = ( column_show_play_count => $column_show_play_count,
15190                @_,  # argument pair list goes here
15191	      );
15192   my $treeview = $treeview_artist; # COLUMN_PLAY_COUNT = 13, but has column position = 1
15193   my $treeviewcolumn = $treeview->get_column(1);  # treeviewcolumn = $tree_view->get_column ($n)
15194   # my $position = $treeviewcolumn->get_sort_column_id;
15195   #print "COLUMN_PLAY_COUNT = ",COLUMN_PLAY_COUNT," \$treeviewcolumn = $treeviewcolumn\n";
15196   $treeview->remove_column($treeviewcolumn) if $treeviewcolumn;
15197
15198   if ( $args{column_show_play_count} ){
15199      # artist play count
15200      $renderer_artist_play_count = Gtk2::CellRendererText->new;
15201      $renderer_artist_play_count->set_data (column => COLUMN_PLAY_COUNT);
15202      $renderer_artist_play_count->set ('font' => $window_font_name, 'xalign' => 0.5, style => 'italic', scale => '0.9');
15203
15204      my $column_artist_play_count = Gtk2::TreeViewColumn->new_with_attributes ($langs{name054}, $renderer_artist_play_count, text => COLUMN_PLAY_COUNT,
15205                                                                             'cell-background-gdk' => COLUMN_COLOR);
15206      $column_artist_play_count->set_resizable ($true);
15207      $column_artist_play_count->set (alignment => 0.0);  # Alignment of the column header text
15208      $column_artist_play_count->set_expand ($false);     # this column expand to max width
15209      $column_artist_play_count->set_min_width (50);
15210      $column_artist_play_count->set (alignment => 0.5);  # Alignment of the column header text
15211      $column_artist_play_count->set_sort_column_id (COLUMN_PLAY_COUNT);
15212      $treeview->insert_column ($column_artist_play_count, COLUMN_PLAY_COUNT);
15213   }
15214
15215   $treeview = $treeview_album;  # COLUMN_PLAY_COUNT = 13, but has column position = 1
15216   $treeviewcolumn = $treeview->get_column(1);  # treeviewcolumn = $tree_view->get_column ($n)
15217   $treeview->remove_column($treeviewcolumn) if $treeviewcolumn;
15218
15219   if ( $args{column_show_play_count} ){
15220      # album play count
15221      $renderer_album_play_count = Gtk2::CellRendererText->new;
15222      $renderer_album_play_count->set_data (column => COLUMN_PLAY_COUNT);
15223      $renderer_album_play_count->set ('font' => $window_font_name, 'xalign' => 0.5, style => 'italic', scale => '0.9');
15224
15225      my $column_album_play_count = Gtk2::TreeViewColumn->new_with_attributes ($langs{name054}, $renderer_album_play_count, text => COLUMN_PLAY_COUNT,
15226                                                                            'cell-background-gdk' => COLUMN_COLOR);
15227      $column_album_play_count->set_resizable ($true);
15228      $column_album_play_count->set (alignment => 0.0);  # Alignment of the column header text
15229      $column_album_play_count->set_expand ($false);     # this column expand to max width
15230      $column_album_play_count->set_min_width (50);
15231      $column_album_play_count->set (alignment => 0.5);  # Alignment of the column header text
15232      $column_album_play_count->set_sort_column_id (COLUMN_PLAY_COUNT);
15233      $treeview_album->insert_column ($column_album_play_count, COLUMN_PLAY_COUNT);
15234   }
15235}
15236
15237sub fixed_toggled {
15238  my ($cell, $path_string) = @_;
15239  my $path = Gtk2::TreePath->new ($path_string); # $path_string is the row number
15240
15241  #my $path = $treeview_play->get_path_at_pos ($path_string);
15242
15243  # this is necessary when the column Track or Length is reordenable or altered.
15244  my $model = $treeview_play->get_model;
15245  my $iter = $model->get_iter($path);
15246  #print ("model = ",$model->get ($iter)," \n");
15247  my $file = $model->get_value($iter,COLUMN_FILE);
15248
15249  #my $row = ($path->get_indices)[0];
15250  my $row = $file-1;
15251
15252  # change the boolean value
15253  $files_info[$row]{rip} ^= 1;  # set new value
15254  $model->set ($iter, COLUMN_RIP, $files_info[$row]{rip});
15255}
15256
15257sub cell_edited { # not more used  15Apr2008
15258  my ($cell, $path_string, $new_text) = @_;
15259  # $path_string is the row number
15260  #print (" path_string = $path_string \n");
15261  my $path = Gtk2::TreePath->new_from_string ($path_string);
15262
15263  my $column = $cell->get_data ("column"); #column number = 2 for title
15264  my $model = $treeview_play->get_model;
15265  my $iter = $model->get_iter ($path);
15266
15267  my $track = $model->get_value($iter,COLUMN_FILE); # track number
15268  #my $track2 = $files_info[$row]{track}; # Note that this don't work
15269  my $row = $track-1;
15270  #my $row = ($path->get_indices)[0];
15271
15272  my $filepath = $files_info[$row]->{filepath};
15273  if ( not -w filename_from_unicode $filepath and $files_info[$row]->{extension_input} ne 'cda' ){
15274     $status_bar->push($context_id, " $filepath ".$langs{msg012} );
15275     return;
15276  }
15277  if ($column == COLUMN_ARTIST) { $entry_artist->set_text( $new_text ); } # this change active sub change_tag
15278  if ($column == COLUMN_TITLE ) { $entry_title->set_text( $new_text );  }
15279
15280  $model->set ($iter, $column, $new_text);
15281  save_tag();
15282
15283  #$status_bar->push($context_id, $langs{msg070} );
15284  $status_bar->push($context_id, "Changes Saved." );
15285}
15286
15287sub unselect_cda { # unselect all tracks; See man Gtk2::ListStore, Gtk2::TreeModel
15288  my $model = $treeview_play->get_model;
15289  my $path; my $iter;
15290
15291  if ( @files_info <= 0 ){ return $false; }
15292  my $val = $force_unselect_all_tracks ? $false : $true;
15293
15294  $model->foreach( sub{  # model is a $ListStore
15295     my($ListStore,$path,$iter,$val) = @_;
15296     my $file = $ListStore->get_value($iter,COLUMN_FILE);
15297     $files_info[$file-1]{rip} = $val;
15298     $ListStore->set ($iter, COLUMN_RIP, $val);
15299     return $false; # to don't abandon the loop
15300  }, $val);
15301
15302  $align_selec->remove ($align_selec->child);
15303
15304  if ( $force_unselect_all_tracks eq $false ){ $align_selec->add ($button_unselec); }
15305  else { $align_selec->add ($button_selec); }
15306
15307  $force_unselect_all_tracks ^= 1; # change boolean variable
15308}
15309
15310
15311# get treeview_play selection/playing/first_row_toggled/first_row files
15312sub get_selection_tree {
15313  return if ( $normalize_button->get_active );
15314
15315  my $selected  = undef;  my $row_playing = undef; my $first_row_toggled = undef;
15316  my $first_row = undef;  my $row = undef;
15317
15318  my $tree_selection = $treeview_play->get_selection;  # $treeview_play->get_selection is an Gtk2::TreeSelection
15319  my $model = $treeview_play->get_model;
15320
15321  my @paths = $tree_selection->get_selected_rows;      # sel = Gtk2::TreePath
15322  #my @sel_rows = map $_->to_string, @paths;
15323  #print "\n s_tree: \@sel_rows = @sel_rows ; np = ",$#paths+1;
15324
15325  if (@paths_button > 0){ # to select the last clicked cell
15326     foreach my $p (@paths_button){$tree_selection->select_path ($p);}
15327  }
15328  my $path = $paths[0]; # get the first selected path only
15329  my $iter = $model->get_iter($path) if $path;
15330  $selected = ($model->get_value($iter,COLUMN_FILE) - 1) if $iter;
15331
15332  my %treeview_play_info = get_treeview_play_informations();
15333  #my @array = @{$treeview_play_info{array_checked}};  # get array_with_files_checked
15334  $first_row_toggled = $treeview_play_info{array_checked}->[0];
15335  $row_playing = $treeview_play_info{row_playing};
15336  $first_row = $treeview_play_info{first_row};
15337
15338  # show info with preference order: $selected -> $row_playing -> $first_row_toggled -> $first_row
15339
15340  $row = undef;
15341  $row = $selected          if (defined $selected);
15342  $row = $row_playing       if (defined $row_playing and not defined $row);
15343  $row = $first_row_toggled if (defined $first_row_toggled and not defined $row);
15344  $row = $first_row         if (defined $first_row and not defined $row);
15345
15346  # only for print purpose!
15347  $selected = 'none'          if not defined $selected;
15348  $row_playing = 'none'       if not defined $row_playing;
15349  $first_row_toggled = 'none' if not defined $first_row_toggled;
15350  $first_row = 'none'         if not defined $first_row;
15351  $row = 'none'               if not defined $row;
15352  #print "\n \$selected          = $selected \n \$row_playing       = $row_playing \n \$first_row_toggled = $first_row_toggled ";
15353  #print "\n \$first_row         = $first_row  \n ------------> \$row = $row\n";
15354
15355  return if $row eq 'none';
15356
15357  my $filepath = $files_info[$row]{filepath};
15358  return unless $filepath;
15359
15360  # save this value that will be used by 'sub change_tag' and 'sub save_tag'
15361  $files_info[0]{get_selection_row} = $row;
15362  my %hash = determine_directory_and_filename_and_extension(row => $row);
15363
15364  # update info
15365  get_info_from_file(%hash)   if ( $files_info[$row]{extension_input} ne 'cda' );   # get mp3/mp4/mpc/... info and set_tag_info
15366  set_tag_info($row)          if ( $files_info[$row]{extension_input} eq 'cda' );
15367  set_output_and_refresh_progress_bar(%hash);
15368  refresh_input_output_arrows();
15369}
15370
15371sub get_treeview_play_informations { # 08Abr2008
15372   my $model = $treeview_play->get_model;
15373   my @array_with_files_checked;
15374   my @array_with_files_unchecked;
15375   my $row_playing = undef;
15376   my $first_row = undef;
15377
15378   $model->foreach( sub{  # model is a $ListStore
15379      my($ListStore,$path,$iter,$val) = @_;
15380      my $row = $ListStore->get_value($iter,COLUMN_FILE) - 1;
15381
15382      # if the row is toggled, then push it into array
15383      if ($files_info[$row]{rip} eq $true){ push @array_with_files_checked,   $row; }
15384      else                                { push @array_with_files_unchecked, $row; }
15385
15386      if ($files_info[$row]{playing}){ $row_playing = $row; }
15387
15388      $first_row = $row if not defined $first_row;
15389
15390      return $false; # to don't abandon the loop
15391   });
15392
15393   return ( array_checked   => \@array_with_files_checked,   # ref to array
15394            array_unchecked => \@array_with_files_unchecked, # ref to array
15395            row_playing     => $row_playing,
15396            first_row       => $first_row,
15397          );
15398}
15399
15400sub set_no_color_for_all { # all tracks are unselected
15401   my $model = $treeview_play->get_model;
15402   $model->foreach( sub{  # model is a $ListStore
15403      my ($ListStore,$path,$iter,$val) = @_;
15404      my $file = $ListStore->get_value($iter,COLUMN_FILE);
15405      my $row = $file - 1;
15406      $files_info[$row]{played} = 0;
15407      $files_info[$row]{playing} = $false;
15408      $model->set ($iter, COLUMN_COLOR, undef) if $iter; # 'untaggle' the played music
15409      return $false; # to don't abandon the loop
15410   });
15411
15412   $model = $treeview_artist->get_model;
15413   $model->foreach( sub{  # model is a $ListStore
15414      my ($ListStore,$path,$iter,$val) = @_;
15415      my $artist = $ListStore->get_value($iter,COLUMN_ARTIST);
15416      #$artists_already_played{uc($artist)} = 0;     # Set False to the artist already played
15417      $model->set ($iter, COLUMN_COLOR, undef) if $iter; # 'untaggle' the played music
15418      return $false; # to don't abandon the loop
15419   });
15420
15421   $model = $treeview_album->get_model;
15422   $model->foreach( sub{  # model is a $ListStore
15423      my ($ListStore,$path,$iter,$val) = @_;
15424      my $album = $ListStore->get_value($iter,COLUMN_ALBUM);
15425      #$albums_already_played{uc($album)} = 0;       # Set False to the album already played
15426      $model->set ($iter, COLUMN_COLOR, undef) if $iter; # 'untaggle' the played music
15427      return $false; # to don't abandon the loop
15428   });
15429}
15430
15431sub color_the_selected_tracks_and_scroll {  # color the selected track, artist and album
15432
15433   my %args = ( select_color   => 'playing', # ('no_color','playing','played')
15434                scroll         => $true,
15435		color_artist   => $true,
15436		color_album    => $true,
15437		set_rip        => $true,
15438		row            => $selected_row,
15439                @_,            # argument pair list goes here
15440	      );
15441   my ($iter,$path,$color,$model);
15442   #while (Gtk2->events_pending()) {Gtk2->main_iteration()}; # to update
15443
15444   if ( $args{color_artist} ){  # color the compound artist name 'art1 & art2' and scroll only to the first artist name
15445      $model = $treeview_artist->get_model;
15446      my @artists = return_artist_name($files_info[$args{row}]); # 'art1 & art2' return (art1,art2)
15447
15448      my $art_playcount_min = $artists[0]; # first artist
15449      foreach my $artist ( @artists ){
15450         last unless defined  $artists_already_played{ uc($artist) };
15451         $art_playcount_min = $artist if ( $artists_already_played{ uc($artist) } < $artists_already_played{ uc($art_playcount_min) } );
15452      }
15453
15454      foreach my $artist ( @artists ){
15455
15456         if    ( $args{select_color} eq 'no_color' and not $artists_already_played{ uc($artist) }  ){  $color = undef;                     }
15457         elsif ( $args{select_color} eq 'playing'                                                  ){  $color = $color_row_playing;        }
15458         #elsif ( $args{select_color} eq 'played'  and $artists_already_played{ uc($artist) } >= 1 ){  $color = $color_row_already_played; }
15459	 else {  $color = $color_row_already_played; }
15460
15461	 ($iter, $path) = get_iter_path_given_track_art_alb ( value => $artist, treeview => 'treeview_artist', column => COLUMN_ARTIST );
15462         $model->set ( $iter, COLUMN_COLOR, $color ) if $iter;  # to color the selected artist line
15463
15464         next if ( uc($artist) ne uc($art_playcount_min) ); # only scroll to the artist with minimum play_count or to the first artist.
15465         $treeview_artist->scroll_to_cell($path, undef, $true ,0.50, 0.50) if ($path and $args{scroll});
15466      }
15467   }
15468
15469   if ( $args{color_album} ){
15470      $model = $treeview_album->get_model;
15471      my $album = return_album_name( $files_info[$args{row}] );  # if no album name, create a guess name from filepath
15472
15473      if    ( $args{select_color} eq 'no_color' and not $albums_already_played{ uc($album) }  ){  $color = undef;                     }
15474      elsif ( $args{select_color} eq 'playing'                                                ){  $color = $color_row_playing;        }
15475      #elsif ( $args{select_color} eq 'played'   ){  $color = $color_row_already_played; }
15476      else                                                                                     {  $color = $color_row_already_played; }
15477
15478      ($iter, $path) = get_iter_path_given_track_art_alb ( value => $album, treeview => 'treeview_album', column => COLUMN_ALBUM );
15479
15480      # Now, scroll to the selected album:
15481      $model->set ( $iter, COLUMN_COLOR, $color ) if $iter;        # to color the selected album line
15482      $treeview_album->scroll_to_cell($path, undef, $true ,0.50, 0.50) if ($path and $args{scroll});
15483   }
15484
15485   $model = $treeview_play->get_model;
15486   ($iter, $path) = get_iter_path_given_track_art_alb ( value => $args{row} + 1, treeview => 'treeview_play', column => COLUMN_FILE );
15487
15488   $treeview_play->scroll_to_cell($path, undef, $true ,0.50, 0.50) if ($path and $args{scroll});
15489
15490   if ( $args{select_color} eq 'no_color' ){
15491      $files_info[$args{row}]{rip} = $true if $args{set_rip};     # change the boolean value
15492      $files_info[$args{row}]{played} = 0;
15493      $files_info[$args{row}]{playing} = $false;
15494      $color = undef;
15495      $treeview_play->set_drag_dest_row ($path, 'into-or-before') if $path; # select the last played track
15496   }
15497   elsif ( $args{select_color} eq 'playing' ){
15498      $files_info[$args{row}]{rip} = $true;
15499      $files_info[$args{row}]{played} = 0;
15500      $files_info[$args{row}]{playing} = $true;
15501      $color = $color_row_playing;
15502   }
15503   elsif ( $args{select_color} eq 'played' ){
15504      $files_info[$args{row}]{rip} = $false;
15505      $files_info[$args{row}]{played} = 1;
15506      $files_info[$args{row}]{playing} = $false;
15507      $color = $color_row_already_played;
15508      $treeview_play->set_drag_dest_row ($path, 'into-or-before') if $path; # select the last played track
15509   }
15510
15511   $model->set ($iter, COLUMN_RIP, $files_info[$args{row}]{rip},  #'untaggle' the played music
15512                       COLUMN_COLOR, $color) if $iter;            # color the $selected_row
15513   #print "file = ",$args{row}," ; select_color = $args{select_color} ; scroll = $args{scroll} \n";
15514
15515   #print "\n\n color_the_selected_tracks_and_scroll --> row = $args{row} ;; select_color = $args{select_color}\n";
15516
15517   $treeview_play->get_selection->unselect_all; # [[$treeview_play->get_selection]] is an Gtk2::TreeSelection
15518   get_selection_tree() if ($args{select_color} eq 'playing');
15519
15520   #count_artists_and_album_already_played() if ($args{select_color} eq 'playing');
15521}
15522
15523#my ($iter, $path) = get_iter_path_given_track_art_alb ( value => $track, treeview => 'treeview_play', column => COLUMN_FILE );
15524
15525sub get_iter_path_given_track_art_alb { # 14Jun2008 - See man Gtk2::ListStore, Gtk2::TreeModel - Without Loops
15526   my %args = (  treeview  => 'treeview_play',     # default value
15527		 value     => $selected_row + 1,   # value can be track/artist/album/.
15528		 column    => COLUMN_FILE,
15529                 @_,       # argument pair list goes here
15530              );
15531   return unless defined $args{treeview};
15532   my ($model, $path, $iter, $row, $value, $value_cmp);
15533   my $debug = $false;  # to print messages
15534
15535   print "--> \$value = $args{value} in $args{treeview}.\n" if $debug;
15536   $value = uc( $args{value} );   # value can be track/artist/album/.
15537
15538   if    ($args{treeview} eq 'treeview_play'  ) { $model = $treeview_play->get_model;   $row = $treeview_play_rows  { $value - 1 };  }  # row = track - 1
15539   elsif ($args{treeview} eq 'treeview_artist') { $model = $treeview_artist->get_model; $row = $treeview_artist_rows{ $value     };  }
15540   elsif ($args{treeview} eq 'treeview_album' ) { $model = $treeview_album->get_model;  $row = $treeview_album_rows { $value     };  }
15541
15542   #$path = Gtk2::TreePath->new ( $row ); # get $path given/knowing the row number
15543   $path = Gtk2::TreePath->new_from_string ($row) if defined $row;
15544   $iter = $model->get_iter ($path) if defined $path; # See 'man Gtk2::TreeModel'
15545
15546   # Now, test the results. If not equal, then use Brute-Force search!
15547   $value_cmp = defined $iter ? $model->get_value($iter,$args{column}) : 'undef_iter';
15548
15549   if ( $value ne uc($value_cmp) or not defined $iter ) { # search_iter_path_using_brute_force
15550      print " Error: \$value = $value not equal to \$value_cmp = $value_cmp in $args{treeview}. Use Brute-Force search!\n" if $debug;
15551      #($iter,$path) = search_iter_path_for_some_element($full_row + 1,$treeview_play,COLUMN_FILE);
15552      ($iter,$path) = search_iter_path_using_brute_force( value => $value, treeview => $args{treeview}, column => $args{column} );
15553   }
15554   print " --- *** ---\n" if ( $args{treeview} eq 'treeview_play' and $debug );
15555   return ( $iter, $path );
15556}
15557
15558#($iter,$path) = search_iter_path_using_brute_force( value => $artist, treeview => 'treeview_artist', column => COLUMN_ARTIST );
15559
15560# Find $iter and $path associeted to some file
15561# This is necessary when the columns Track/Artist/Album/Play Count/ ... are reordenable or altered
15562
15563sub search_iter_path_using_brute_force { # 14Jun2008 - See man Gtk2::ListStore, Gtk2::TreeModel - Using Loop '$model->foreach('
15564   my %args = (  treeview  => 'treeview_play',   # default value
15565		 value     => $selected_row + 1, # value can be track/artist/album/.
15566		 column    => COLUMN_FILE,
15567                 @_,       # argument pair list goes here
15568              );
15569   return unless defined $args{treeview};
15570   my ($model, $path, $iter);
15571
15572   if    ($args{treeview} eq 'treeview_play'  ) { $model = $treeview_play->get_model;   %treeview_play_rows   = (); }
15573   elsif ($args{treeview} eq 'treeview_artist') { $model = $treeview_artist->get_model; %treeview_artist_rows = () ;}
15574   elsif ($args{treeview} eq 'treeview_album' ) { $model = $treeview_album->get_model;  %treeview_album_rows  = () ;}
15575
15576   my $row = 0;
15577
15578   $model->foreach( sub{                      # Brute-Force search
15579      my($ListStore,$path2,$iter2) = @_;      # model is a $ListStore
15580      my $value_found = $ListStore->get_value($iter2,$args{column});
15581      #my $rrow = ($path2->get_indices)[0];
15582
15583      if ( uc($args{value}) eq uc($value_found) ){  # if the values are equals, the $iter is right
15584         $path = Gtk2::TreePath->new ($row);
15585         $iter = $model->get_iter($path);
15586	 #print "row = $row ; value = $args{value} ; value_found = $value_found ; column = $args{column}\n";
15587	 #return $true; # abandon the loop 'foreach'
15588      }
15589
15590      # Fill with the new values before to alter some column.
15591      if    ( $args{treeview} eq 'treeview_play'   ) { $treeview_play_rows  { $value_found - 1    } = $row; } # $value_found is track = row + 1
15592      elsif ( $args{treeview} eq 'treeview_artist' ) { $treeview_artist_rows{ uc( $value_found )  } = $row; }
15593      elsif ( $args{treeview} eq 'treeview_album'  ) { $treeview_album_rows { uc( $value_found )  } = $row; }
15594
15595      #if    ( $args{treeview} eq 'treeview_artist' ) { print "\$treeview_artist_rows{ $value_found } = $treeview_artist_rows{ uc( $value_found ) } \n"; }
15596      #elsif ( $args{treeview} eq 'treeview_album'  ) { print "\$treeview_album_rows{  $value_found } = $treeview_album_rows{  uc( $value_found ) } \n"; }
15597
15598      $row += 1;
15599      return $false; # to don't abandon the loop
15600   } );
15601
15602   return ($iter,$path);
15603}
15604
15605sub search_iter_path_for_some_element { # See man Gtk2::ListStore, Gtk2::TreeModel - Not more used
15606   my ($value,$treeview,$column) = @_;  # value can be track/artist/album/...
15607   my $iter; my $path;
15608   my $model = $treeview->get_model;
15609
15610   my $row = 0;
15611
15612   $model->foreach( sub{                      # Brute-Force search
15613      my($ListStore,$path2,$iter2) = @_;      # model is a $ListStore
15614      my $value_found = $ListStore->get_value($iter2,$column);
15615
15616      # $value can be a number or character
15617      if ( uc($value) eq uc($value_found) ){  # if the values are equals, the $iter is right
15618         $path = Gtk2::TreePath->new ($row);
15619         $iter = $model->get_iter($path);
15620	 #print "row = $row ; value = $value ; value_found = $value_found ; column = $column\n";
15621	 return $true; # abandon the loop 'foreach'
15622      }
15623
15624      $row += 1;
15625      return $false; # to don't abandon the loop
15626   } );
15627   return ($iter,$path);
15628}
15629
15630
15631sub REAPER { # To reap/eliminate dead children ;  see perlipc; Programming Perl, 3a Ed., Chap.16
15632   # If a second child dies while in the signal handler caused by the
15633   # first death, we won't get another signal. So must loop here else
15634   # we will leave the unreaped child as a zombie. And the next time
15635   # two children die we get another zombie. And so on.
15636   while ( waitpid(-1,WNOHANG) > 0 ){}  # waitpid return "-1" if there is no such child process.
15637   $SIG{CHLD} = \&REAPER;  # still loathe sysV
15638}
15639
15640my $selected_row_last_value = -10; #some absurd value
15641my $extension_last_value = "";
15642
15643sub play_selection {  # stop and play
15644  my %args = (  go_back   => $false, # default value
15645                skip      => undef,
15646                @_,       # argument pair list goes here
15647  );
15648  $extension_last_value = $files_info[$selected_row]{extension_input};
15649  stop_playing_music();
15650  $status_bar->push($context_id, "");
15651
15652  if ( not defined $args{skip} ) {
15653     # when go_back, the selected_row is already known from @rows_already_played
15654     if ( not $args{go_back} ){
15655        $selected_row = choose_one_row_to_play();
15656     }
15657     # $selected_row == -1 when there is none selected track to play
15658     if ( $selected_row == -1){
15659        if ($loop_tracks eq $false){
15660	    $status_bar->push($context_id, $langs{msg022} );
15661            $go_forward = $false;
15662	    show_time();
15663        }
15664        else {
15665            $force_unselect_all_tracks = $false; # to select all tracks
15666            unselect_cda();  # subroutine that select all tracks
15667            $selected_row = choose_one_row_to_play();
15668        }
15669	@rows_already_played = ();
15670	$selected_row_last_value = -10;
15671	set_no_color_for_all();
15672     }
15673  }
15674  if ( $selected_row == -1 ){ return $false; } # If there is no selected track
15675
15676  $total_time = $files_info[$selected_row]{length} if $selected_row != -1 ; # 2:56:34   02:32
15677  $total_time = time_to_sec($total_time);
15678
15679  # when cda is playing and the next selected_row is not cda
15680  if ( $extension_last_value eq 'cda' and $files_info[$selected_row]{extension_input} ne 'cda' ){
15681     stop_cda(); # don't stop if the next track have cda extension_input
15682  }
15683
15684  # xine -A arts -V none -I -H "music.mp3"
15685  # mac "zzz.ape"  - -d | play -t wav -d /dev/audio -
15686
15687  # see <man SDL> or <man SDL::Cdrom>
15688  #use SDL::Cdrom;
15689  #my $cdrom = SDL::Cdrom->new(0);
15690  #$cdrom->play();
15691
15692  #my $music = $model->get_value($iter,COLUMN_FILEPATH); #print "music = $music\n";
15693  my $music = $files_info[$selected_row]{filepath};  #print "music = $music\n";
15694  my $buffer = 4 * 1024; # 4MB
15695  # $device_type = 'arts'; # oss, alsa, arts, esd, jack, nas
15696  # $device_path = '/dev/sound/adsp';
15697  my @exec_argv;
15698  if ($Frequency == 0){ $Frequency = $files_info[$selected_row]{frequency}; }
15699  if ( not -e filename_from_unicode $music and $files_info[$selected_row]{extension_input} ne 'cda'  ){
15700     $files_info[$selected_row]{remove} = $true;
15701     go_forward();
15702     return;
15703  }
15704  if ( defined $args{skip} ) { $count = $args{skip}; } # determine the start time to be played
15705
15706  if ($files_info[$selected_row]{extension_input} eq 'mp3' and ($mpg123_path or $mpg321_path or $madplay_path or $mplayer_path) ){
15707
15708	# $totalSeconds  = $Total_samples/$Frequency  -->  $Total_samples = $totalSeconds * $Frequency;
15709	# $Total_samples = $frame_count * 32*36       -->  $frame_count = $Total_samples / 32*36 = ($totalSeconds * $Frequency) / 1152;
15710	my $frame = sprintf ('%.0f', ($count * $Frequency) / 1152 );
15711
15712        if    ($mpg321_path  and $player_mp3 eq "mpg321" ){ @exec_argv = ($mpg321_path, '-o', $device_type, '-b', $buffer, '-q', '-k', $frame ); }
15713	elsif ($mpg123_path  and $player_mp3 eq "mpg123" ){ @exec_argv = ($mpg123_path, '-q', '-k', $frame );                                    }
15714	elsif ($madplay_path and $player_mp3 eq "madplay"){ @exec_argv = ($madplay_path, '-Q', '-s', $count, '-o', $device_path );               }
15715	elsif ($mplayer_path and $player_mp3 eq "mplayer"){ @exec_argv = ($mplayer_path, '-ao', "$device_type,:mmap:noblock", '-cache',
15716	                                  $buffer, '-vo', 'null', '-really-quiet', '-slave','-noconsolecontrols', '-nojoystick','-ss', $count ); }
15717
15718	push  @exec_argv, filename_from_unicode $music;
15719  }
15720  elsif ( $files_info[$selected_row]{extension_input} eq 'mp4' and $mplayer_path ){
15721
15722	@exec_argv = ($mplayer_path, '-ao', "$device_type,:mmap:noblock", '-cache', $buffer, '-vo', 'null',
15723	              '-really-quiet', '-slave', '-noconsolecontrols', '-nojoystick','-ss', $count, filename_from_unicode $music );
15724  }
15725  elsif ($files_info[$selected_row]{extension_input} eq 'mpc' and ($mpcdec_path or $mplayer_path) ){
15726
15727     if    ($mplayer_path and $player_mpc eq "mplayer" ){
15728        @exec_argv = ($mplayer_path, '-ao', "$device_type,:mmap:noblock", '-cache', $buffer, '-vo', 'null',
15729	              '-really-quiet', '-slave','-noconsolecontrols', '-nojoystick','-ss', $count, filename_from_unicode $music );
15730     }
15731     elsif ($mpcdec_path and $player_mpc eq "mpcdec" ){
15732        @exec_argv = ($mpcdec_path, '--silent', '--start', $count, filename_from_unicode $music, $device_path );
15733     }
15734  }
15735  elsif ($files_info[$selected_row]{extension_input} eq 'ogg' and ($ogg123_path or $mplayer_path) ){
15736
15737     if    ($mplayer_path and $player_ogg eq "mplayer" ){
15738        @exec_argv = ($mplayer_path, '-ao', "$device_type,:mmap:noblock", '-cache', $buffer, '-vo', 'null',
15739	              '-really-quiet', '-slave','-noconsolecontrols', '-nojoystick','-ss', $count, filename_from_unicode $music );
15740     }
15741     elsif ($ogg123_path and $player_ogg eq "ogg123" ){
15742        @exec_argv = ($ogg123_path, '-d', $device_type, '-b', $buffer, '-q', '-k', $count, filename_from_unicode $music );
15743     }
15744  }
15745  elsif ($files_info[$selected_row]{extension_input} eq 'ape' and ( ($ape_path and ($play_path or $aplay_path)) or $mplayer_path ) ){
15746
15747     if    ($mplayer_path and $player_ape eq "mplayer" ){
15748        @exec_argv = ($mplayer_path, '-ao', "$device_type,:mmap:noblock", '-cache', $buffer, '-vo', 'null',
15749	              '-really-quiet', '-slave','-noconsolecontrols', '-nojoystick','-ss', $count, filename_from_unicode $music );
15750     }
15751     elsif ( ($ape_path and $play_path) and $player_ape eq "mac" ){
15752        # mac "zzz.ape"  - -d | play -t wav -d /dev/audio -
15753        #@exec_argv = ($ape_path, filename_from_unicode $music, '-', '-d', '|', '/usr/bin/play', '-t', 'wav', '-');
15754	#my $cmd = "$ape_path \"$music\" - -d | $play_path -t wav -d $device_path - ";
15755	my $cmd = "$ape_path \"$music\" - -d | $play_path -t wav - ";
15756	@exec_argv = ( filename_from_unicode "$cmd" );
15757     }
15758     elsif ( ($ape_path and $aplay_path) and $player_ape eq "aplay" ){
15759        # mac "zzz.ape"  - -d | play -t wav -d /dev/audio -
15760        #@exec_argv = ($ape_path, filename_from_unicode $music, '-', '-d', '|', '/usr/bin/play', '-t', 'wav', '-');
15761	#my $cmd = "$ape_path \"$music\" - -d | $play_path -t wav -d $device_path - ";
15762	my $cmd = "$ape_path \"$music\" - -d | $aplay_path -N -q - ";
15763	@exec_argv = ( filename_from_unicode "$cmd" );
15764     }
15765  }
15766  elsif ($files_info[$selected_row]{extension_input} eq 'flac' and ($flac123_path or $mplayer_path) ){
15767
15768     if    ($mplayer_path and $player_flac eq "mplayer" ){
15769        @exec_argv = ($mplayer_path, '-ao', "$device_type,:mmap:noblock", '-cache', $buffer, '-vo', 'null',
15770	              '-really-quiet', '-slave','-noconsolecontrols', '-nojoystick','-ss', $count, filename_from_unicode $music );
15771     }
15772     elsif ($flac123_path and $player_flac eq "flac123" ){
15773        $count = 0;
15774        @exec_argv = ($flac123_path, '-d', $device_type, '-q', filename_from_unicode $music );
15775     }
15776  }
15777  elsif ($files_info[$selected_row]{extension_input} eq 'wav' and ($aplay_path or $mplayer_path) ){
15778
15779     if    ($mplayer_path and $player_wav eq "mplayer" ){
15780        @exec_argv = ($mplayer_path, '-ao', "$device_type,:mmap:noblock", '-cache', $buffer, '-vo', 'null',
15781	              '-really-quiet', '-slave','-noconsolecontrols', '-nojoystick','-ss', $count, filename_from_unicode $music );
15782     }
15783     elsif ($aplay_path and $player_wav eq "aplay" ){
15784        $count = 0;
15785        @exec_argv = ($aplay_path, '-N', '-q', filename_from_unicode $music );
15786     }
15787     elsif ($play_path and $player_wav eq "play" ){
15788        $count = 0;
15789        @exec_argv = ($play_path, filename_from_unicode $music );
15790     }
15791  }
15792
15793  if ($files_info[$selected_row]{extension_input} ne 'cda' and @exec_argv >=1){
15794        $SIG{CHLD} = \&REAPER; # To reap/eliminate dead children ;  see perlipc
15795        # see man perlfunc, perlfork, perlipc ; Learning Perl 14.6, 3a Ed. ; Perl Cookbook 16.10, 2a. Ed
15796        defined($pid_player = fork) or die "Can't fork: $!";
15797        if( $pid_player == 0 ){
15798           #Child process is here ; child process has a zero value
15799	   open \*STDIN, '>/dev/null';
15800	   open \*STDOUT,'>/dev/null';
15801	   open \*STDERR,'>/dev/null';
15802	   exec @exec_argv;  # see perlfunc
15803	   die "cannot play audio: $!\n";
15804        }
15805        #Parent process is here ; parent process has a nonzero value
15806        #waitpid($pid,0);
15807  }
15808  #print "count = $count ; pid_player = $pid_player; exec_argv = @exec_argv\n";
15809
15810  if ($files_info[$selected_row]{extension_input} eq 'cda'){
15811        # umount the audio cd drive if possible. This is need to play EXTRA CD.
15812        exec_cmd_system("umount $audiodevice_path" );
15813
15814        if ($cdplayer eq "cdcd"){
15815	   my @cmd = ( $cdcd_path, '-d', $audiodevice_path, 'play', $selected_row + 1, $selected_row + 1 );
15816           exec_cmd_system2(@cmd);
15817        }
15818        elsif ( $cdplayer eq "gnormalize::cdplay" ){
15819	   gnormalize::cdplay::play_cdrom_msf(Track => $selected_row + 1);
15820        }
15821        elsif ( $cdplayer eq "Audio::CD" ){ # libcdaudio  ;; Audio::CD ;; see <man Audio::CD>
15822	   $audiocd = Audio::CD->init("$audiodevice_path");
15823           $audiocd->play_track($selected_row + 1, $selected_row + 1);
15824        }
15825	#print "using command to play cdrom: cmd = $cmd\n";
15826  }
15827  # draw scrolling text
15828  if ($selected_row != $selected_row_last_value){ show_scrolling_text(); }
15829  $selected_row_last_value = $selected_row;
15830
15831  $playing_music = $true;
15832  #while (Gtk2->events_pending()) {Gtk2->main_iteration()};
15833
15834  # Now, scroll to the selected artist
15835  color_the_selected_tracks_and_scroll( select_color => 'playing' );
15836  count_artists_and_album_already_played(update_model_artist => $true, update_model_album => $true);
15837
15838  # see <man Glib::MainLoop>
15839  # integer = Glib::Timeout->add ($interval, $callback, $data=undef, $priority=G_PRIORITY_DEFAULT)
15840  if ( defined($timer) ){ Glib::Source->remove($timer);}  # to not duplicate the MainLoop
15841  $timer = Glib::Timeout->add (500, \&change_count, $total_time); # 250 milliseconds = 0.250 second
15842  # integer = Glib::IO->add_watch ($fd, $condition, $callback, $data=undef, $priority=G_PRIORITY_DEFAULT)
15843
15844}
15845# use constant INTERVAL	=> 1000; # for cd player; 250 milliseconds = 0.250 second
15846
15847sub choose_filter { # filter can be artist, album, genre, ...  See 'sub change_filter'
15848   my $hash = shift;
15849   return unless $use_filter; # $use_filter == 0
15850
15851   my $play_tracks_from_different_albums  = $true  if $use_filter == 1;
15852   my $play_tracks_from_different_artists = $true  if $use_filter == 2;
15853   my $play_tracks_from_different_genres  = $true  if $use_filter == 3;
15854   my $play_tracks_from_different_years   = $true  if $use_filter == 4;
15855
15856   return return_album_name ($hash) if $play_tracks_from_different_albums  and defined $hash->{album};
15857   return return_artist_name($hash) if $play_tracks_from_different_artists and defined $hash->{artist};
15858   return $hash->{genre}            if $play_tracks_from_different_genres  and defined $hash->{genre};
15859   return $hash->{year}             if $play_tracks_from_different_years   and defined $hash->{year};
15860}
15861
15862      # Making Hashes of Arrays (Perl Cookbook, Chap. 11.2)
15863      # $hash{'a artist'} = [3,4,7, ..., n]; # anonymous array
15864      # To append a new value: push @{ $hash{'a artist'} }, $row;
15865      # To obtain an array without repeated values: my %unique = map {$_ => 1}, @numbers;
15866
15867# Make array with all selected rows that will be played. 'sub choose_one_row_to_play' returns the selected_row number.
15868sub choose_one_row_to_play { # 06Jun2008
15869   my $random_row = -1; # get by $selected_row
15870   my $debug = $false;  # to print messages
15871
15872   my %treeview_play_info = get_treeview_play_informations();
15873   my @rows_checked = @{$treeview_play_info{array_checked}};  # get array_with_files_checked
15874   #my $first_row_toggled = $rows_to_play[0];
15875   #my $row_playing = $treeview_play_info{row_playing};
15876   #my $first_row = $treeview_play_info{first_row};
15877
15878   # The array '@rows_unchecked' will be used to get rows already played, but '@rows_already_played' would be used too.
15879   # '@rows_unchecked' have more informations, because '@rows_already_played' is always erased whenever quit from gnormalize.
15880   my @rows_unchecked = @{$treeview_play_info{array_unchecked}};  # To fill with the unchecked rows;
15881   print "\n\@rows_unchecked = @rows_unchecked\n" if $debug;
15882
15883   my @rows_to_play = @rows_checked;
15884
15885   if ( $use_filter and @rows_checked >= 1 and @rows_unchecked >= 1 ) {  # Add loops with 2*O(N) efficiency order.
15886
15887      my %available_artists = ();       # To find all available artists to play
15888      foreach my $row (@rows_checked){  # It has 1*O(N) efficiency order.
15889         my @artists = map { uc($_) } choose_filter($files_info[$row]);
15890	 foreach my $art (@artists) { $available_artists{$art}++; }  # Add +1 to the available_artists
15891      }
15892      my $num_dif_artists = keys %available_artists;      # How can I know how many entries are in a hash? See 'man perlfaq4'.
15893
15894      my %artists_played = ();                            # Used to filter, such that 'filter' can be artist, album, genre, ...
15895      #foreach my $row_played (@rows_already_played) {
15896      foreach my $row_played (@rows_unchecked) { # Permutations like (A B C D)(C A D B)(D A B C)(D ...
15897
15898         my @art_played = map { uc($_) } choose_filter($files_info[$row_played]);
15899	 foreach my $art (@art_played) { $artists_played{$art}++; }  # Add +1 to the artist already played.
15900	 if ($debug) { foreach my $art (@art_played) { print "\n\$row_played = $row_played ; played $artists_played{$art} time(s) --> {$art} \n"; } }
15901      }
15902
15903      my $minimum = $#rows_checked + 100;             # Choose one great value
15904      foreach my $art ( keys %available_artists ) {   # To find the minimum play times value
15905	 if (not $artists_played{$art}) { $minimum = 0 ; last; }   # played 0 times
15906	 $minimum = $artists_played{$art} if $artists_played{$art} < $minimum;
15907      }
15908      print "\n\$minimum = $minimum ; " if $debug;
15909
15910      my @last_artists_played; # To avoid to play the same artist/album sequentially
15911      if (@rows_already_played >= 1){
15912         my $last_row_played  = $rows_already_played[$#rows_already_played];
15913         @last_artists_played = map { uc($_) } choose_filter($files_info[$last_row_played]);
15914         foreach my $last_artist (@last_artists_played) { $artists_played{$last_artist}++ if ($num_dif_artists > @last_artists_played); }
15915      }
15916
15917      @rows_to_play = ();                  # Fill with rows to play
15918      while (@rows_checked >=1 and not @rows_to_play >= 1) {   # if not select any rows_to_play, then $minimum += 1;
15919         foreach my $row (@rows_checked){  # It has 1*O(N) efficiency order.
15920
15921	    my @artists = map { uc($_) } choose_filter($files_info[$row]);
15922	    foreach my $art (@artists) {
15923	       if (not $artists_played{$art} or $artists_played{$art} <= $minimum) { push @rows_to_play, $row; last; } # use 'last' to not duplicate rows.
15924	    }
15925         }
15926         print "\@rows_to_play = @rows_to_play ; \@last_artists_played = @last_artists_played ; \$num_dif_artists = $num_dif_artists ; \$minimum_new = $minimum\n" if $debug;
15927         $minimum += 1;
15928      }
15929   }
15930
15931   # to remove one element from front of @array -- see perlfunc : splice
15932   if ( @rows_to_play >= 1 ){  # exist at least one element
15933      if ( $play_random eq $true ){
15934	     my $rand = int( rand(@rows_to_play) ); # pick one random element: 0<= rand < @rows_to_play number
15935	     #print " rand[0 to $#rows_to_play] = $rand ;; all rows = @rows_to_play\n";
15936	     $random_row = splice(@rows_to_play, int( $rand ), 1); # pull one random element of @array
15937      }
15938      else { $random_row = splice(@rows_to_play, 0, 1);} # pull the first element of @array
15939   }
15940
15941   #print " random_row = $random_row ;; rows_to_play = @rows_to_play\n";
15942   return $random_row ; # return $row that will be played
15943}
15944
15945
15946# This is a loop for  "$timer = Glib::Timeout->add (250, \&change_count, $total_time);"
15947# at sub play_selection the show the time when button "play" is pressed.
15948my $count_blink = 0; # blink when paused.
15949sub change_count { # the main loop
15950
15951  # play all tracks  - go to next track
15952  if ( ($count >= $total_time or $go_forward eq $true) and $playing_music and $files_info[$selected_row]{extension_input} eq 'cda') {
15953     go_forward();
15954  }
15955  elsif ( defined $pid_player and $playing_music and $files_info[$selected_row]{extension_input} ne 'cda') {
15956     if ( waitpid($pid_player, WNOHANG) < 0 or $go_forward eq $true ){ go_forward(); }
15957  }
15958  if ( $go_back eq $true and @rows_already_played > 0 ) {
15959     go_back();
15960  }
15961  $count = $count + 0.500 if ( not $pause ); # count is a number for seconds elapsed.
15962
15963  $count_blink += 0.500;
15964  get_current_track_and_time() if ( $files_info[$selected_row]{extension_input} eq 'cda' );
15965  # need show time only if the count change its value.
15966  #if ( int($last_count) != int($count) or int($count) == 0 ){ show_time(); }
15967  show_time();
15968
15969  return $true; # if return is $false; then stop Glib::Timeout
15970}
15971
15972sub go_forward {
15973     $go_forward = $true;
15974     my $row_played = $selected_row;
15975
15976     push @rows_already_played, $row_played;  # if the row was played completely, then push it to @rows_already_played
15977     #print "--> rows_already_played = @rows_already_played  \n";
15978
15979     # To color the selected artist:
15980     color_the_selected_tracks_and_scroll( select_color => 'played', scroll => $false, row => $row_played );
15981
15982     play_selection(go_forward => $true);   # stop and play
15983     $go_forward = $false;
15984}
15985
15986sub go_back {
15987     $go_back = $true;
15988     my $row_playing = $selected_row;
15989
15990     #print "antes -- rows_already_played = @rows_already_played  \n";
15991     $selected_row = splice(@rows_already_played, -1);      # remove the last element from @array
15992     #print "depois -- rows_already_played = @rows_already_played ;; selected_row = $selected_row\n";
15993
15994     # To color the selected artist:
15995     color_the_selected_tracks_and_scroll( select_color => 'no_color', scroll => $false, row => $row_playing );
15996
15997     play_selection(go_back => $true); # stop and play
15998     $go_back = $false;
15999}
16000
16001sub get_current_track_and_time {
16002  my $current_track; my $frame; my $status; my $current_position;
16003  my $abs_start_frame = $start_sector[$selected_row] + 150;      # absolute start frame, 150 is the offset (gap) of first frame.
16004  my $abs_final_frame = $start_sector[$selected_row + 1] + 150;  # absolute final frame for the selected track
16005  if ( $selected_row == -1 ){ return $true; } # If there is no selected row
16006
16007  if ( $cdplayer eq "Audio::CD" ){
16008     if ( not $have_current_track ){ return $true; }
16009     my $info = $audiocd->stat;
16010     $status = $info->mode;
16011     if ( $status == 0 ){ $playing_music = $true;    }
16012     if ( $status == 1 ){ $pause = $true; return $true; } else { $pause = $false; }
16013     if ( $status == 2 ){ $go_forward = $true; } # completed
16014     if ( $status == 3 ){ stop_playing_music();  } # stop
16015     if ( $status > 3 or $status < 0 ){ entry_cda_change();  }  # Error
16016
16017     # status = 0 (playing) ;;  status = 1 (paused) ;;  status = 2 (completed)
16018     # status = 3 (NOSTATUS) ;; status = 4 (INVALID) ;; status = 5 (ERROR) ;; See /usr/include/cdaudio.h
16019
16020     my ($minutes, $seconds, $frames) = $info->time; # Returns the current disc play time
16021     $current_position = ($minutes * 60 + $seconds) * 75 + $frames; # absolute frame position
16022  }
16023  elsif ( $cdplayer eq "gnormalize::cdplay" ){
16024     my $info = gnormalize::cdplay::Info();
16025     if ( not $info ){ entry_cda_change(); return; } # Get Status Error
16026     $status = $info->{status};
16027
16028     if ( $status eq 'play'      ){ $playing_music = $true;    }
16029     if ( $status eq 'paused'    ){ $pause = $true; return $true; } else { $pause = $false; }
16030     if ( $status eq 'completed' ){ $go_forward = $true;    } # completed
16031     if ( $status eq 'no_status' ){ stop_playing_music();   } # stop
16032
16033     #my($minutes, $seconds) = ( $info->{min}, $info->{sec} ); # don't use this because it shows regressive time!
16034     $current_position = $info->{frame_abs}; # absolute frame position
16035  }
16036  elsif ( $cdplayer eq "cdcd" ){ # not used, because cdcd consume so much process resource
16037     return $true;
16038     # status: Playing n�7 01:10.25/track 20:28.52/disc
16039     my $cmd = "cdcd -d $audiodevice_path status";
16040     my $status = exec_cmd_system($cmd);
16041     if ( $status =~ /Playing n�(\d+)\s+(\d*:?\d+:\d+[\.,]\d+)\/track\s+(\d*:?\d+:\d+[\.,]\d+)\/disc/i ){
16042        my $current_track = $1;
16043        $count = time_to_sec($2);
16044	#print " track = $1 ;; 2 = $2 ;; time_to_sec = ",time_to_sec($2)," ;; 3 = $3 \n";
16045     }
16046     if ($status =~ /Stopped/i){ stop_playing_music(); }
16047  }
16048
16049  $frame = $current_position - $abs_start_frame;
16050  $frame = 0 if $frame < 0;
16051  #$count = number_value(sprintf("%02.2f", ($frame / 75 )));
16052  $count = int($frame / 75) if not $pause; # get the new value, number of seconds = frame / 75
16053
16054  #$current_track = $info->{current_track}; # this is not correct on the boundary region between tracks.
16055  $current_track = get_current_track();     # Recompile and install Audio-CD-0.04-changed.tar.gz  for "Audio::CD"
16056
16057  #print "count=$count;current_track=$current_track;selected_row=$selected_row;status=$status;".
16058  #      "frame=$frame;current_position=$current_position\n";
16059
16060  my $before_end       = $abs_final_frame - 2 * 75; # remaining 2 sec. See sub play_cdrom_msf().
16061  my $start_next_frame = $abs_final_frame + 40;
16062
16063  #if ( $current_position > $before_end and $current_track == $selected_row ) { $go_forward = $true; }
16064  # The boundary region where the regressive time and next track is showed. I want to avoid this.
16065  if ( $current_position > $before_end and $current_position <= $start_next_frame ){ $current_track = $selected_row + 1; }
16066
16067  if ( $current_track != $selected_row + 1 ){ # If another program changes the selected track.
16068     my ($iter, $path) = get_iter_path_given_track_art_alb ( value => $current_track, treeview => 'treeview_play', column => COLUMN_FILE );
16069
16070     $treeview_play->scroll_to_cell($path, undef, $true ,0.50, 0.50) if $path;
16071     $count = 0;
16072  }
16073  $selected_row = $current_track - 1 if $current_track > 0;
16074  return $true; # if return is $false; then stop the Glib::Timeout loop
16075}
16076
16077sub get_current_track{
16078   my $total_track = $#start_sector + 1;
16079   #my $total_track = $toc_audiocd->[0]->{total_track};
16080   my $current_track = $total_track;     # start value is the total audio track number
16081   my $current_position;
16082   my $offset_frame = 150;
16083
16084   if ( $cdplayer eq "gnormalize::cdplay" ){
16085      $current_position = gnormalize::cdplay::Info()->{frame_abs}; # absolute frame position
16086   }
16087   elsif ( $cdplayer eq "Audio::CD" ){
16088      my $info = $audiocd->stat;
16089      my ($minutes, $seconds, $frames) = $info->time; # Returns the current disc play time
16090      $current_position = ($minutes * 60 + $seconds) * 75 + $frames;
16091   }
16092
16093   for(my $track=1; $track < $total_track; $track++){
16094      #if ($current_position < $toc_audiocd->[$track]->{frames_abs}){ $current_track = $track; last; }
16095      # 150 is the offset (gap) of first frame. So (absolute frames) = ($start_sector[$track] + 150) .
16096      if ( $current_position < $start_sector[$track] + $offset_frame ){ $current_track = $track; last; }
16097   }
16098   return $current_track;
16099}
16100
16101
16102my $pct_last_value = 0;
16103
16104sub show_time{       # show time on display
16105  if ( $playing_music eq $false ){ return; }
16106
16107  $total_time = $files_info[$selected_row]{length} if $selected_row != -1 ;
16108  $total_time = time_to_sec($total_time);
16109
16110  my $show_time  = $total_time; # one track total time
16111  my $show_count = $count;      # relative position
16112
16113  if ($show_time > 0){
16114     # draw lapsed time ; show only not repeated value
16115     my $pct = number_value( sprintf("%.2f", $show_count/$show_time) );
16116     if ($pct != $pct_last_value and $pct <= 1){ draw_lapsed_time($da, undef, "$pct"); }
16117     $pct_last_value = $pct;
16118  }
16119
16120  if ($selected_row != $selected_row_last_value){ show_scrolling_text(); }
16121  $selected_row_last_value = $selected_row;
16122
16123  stop_playing_music() if (not defined $pid_player and $files_info[$selected_row]{extension_input} ne 'cda');
16124
16125  if ( $show_time_abs and $cdplayer eq "gnormalize::cdplay" and $files_info[$selected_row]{extension_input} eq 'cda' ){
16126     #my $current_position = gnormalize::cdplay::Info()->{frame_abs}; # absolute frame position
16127     $show_count = int ( ( gnormalize::cdplay::Info()->{frame_abs} - $toc_audiocd->[0]->{frames_abs} ) / 75 );
16128     $show_time  = int (($toc_audiocd->[0]->{total_frames} / 75 )); # absolute time
16129  }
16130  elsif ( $show_time_abs and $cdplayer eq "Audio::CD" and $files_info[$selected_row]{extension_input} eq 'cda' ){
16131     my $info = $audiocd->stat;
16132     my ($minutes, $seconds) = $info->time; # Returns the current disc play time
16133     $show_count = time_to_sec("$minutes:$seconds");
16134     ($minutes, $seconds) = $info->length;  # Returns the disc length time
16135     $show_time  = time_to_sec("$minutes:$seconds");
16136  }
16137
16138  my ($hour,$min,$sec) = sec_to_time($show_count);                     # current time
16139  my ($hour_r,$min_r,$sec_r) = $show_time >= $show_count ? sec_to_time($show_time - $show_count) : sec_to_time( 0 );  # time remaining
16140  my ($hour_t,$min_t,$sec_t) = sec_to_time($show_time);                # total time
16141
16142  if ( $pause eq $true and $count_blink % 2 == 0 ){ # blink - piscar
16143     $min   = -2; $min_r = -2;
16144     $count_blink = 0 if $count_blink > 1000;
16145  }
16146
16147  # print "--> hour = $hour ;; min = $min ;; sec = $sec ;; total = $show_time ;; count = $show_count\n";
16148  if ( $show_time_remaining eq $false ){ draw_play_time($da, undef, ($min,$sec,$hour) ); }
16149  else{ draw_play_time($da, undef, ($min_r,$sec_r,$hour_r) ); }
16150
16151  return $true;
16152}
16153
16154sub stop_cda { # don't stop if the next track have cda extension_input
16155
16156   if ($cdplayer eq "cdcd") {
16157      my $cmd = "$cdcd_path -d $audiodevice_path stop ";
16158      exec_cmd_system($cmd);
16159   }
16160   elsif ( $cdplayer eq "gnormalize::cdplay" ){
16161      gnormalize::cdplay::stop_cdrom();
16162   }
16163   elsif ( $cdplayer eq "Audio::CD" ){ # libcdaudio  ;; Audio::CD ;; see <man Audio::CD>
16164      $audiocd = Audio::CD->init("$audiodevice_path");
16165      $audiocd->stop;
16166   }
16167   $playing_music = $false;
16168
16169   if (@rows_already_played > 0 and 1==2){ #debug
16170      print "extension_last_value = $extension_last_value ; extension_input = $files_info[$selected_row]{extension_input}\n";
16171   }
16172}
16173
16174sub stop_playing_music{
16175
16176   my %args = ( select_color   => 'no_color', # ('no_color','playing','played')
16177                scroll         => $false,
16178		set_rip        => $false,
16179		change_color   => $true,
16180                @_,            # argument pair list goes here
16181	      );
16182   my $cmd;
16183   if ( not $playing_music ){ # update the 'cdrom:' entry only if audio cd is not playing
16184      $audiodevice_path = $entry_cda->get_text(); # /dev/hdc or /dev/cdrom or ...
16185   }
16186   if ( defined($timer) ){ Glib::Source->remove($timer);} # quit loop, see <man Glib::MainLoop>
16187
16188   return unless defined $files_info[$selected_row]{extension_input};
16189
16190   if ( $playing_music and not ($go_back or $go_forward) and $files_info[$selected_row]{extension_input} eq 'cda' ){
16191      stop_cda();  # don't stop if the next track have cda extension_input
16192   }
16193
16194   if ($files_info[$selected_row]{extension_input} ne 'cda' and defined $pid_player){
16195      #print "pid_player = $pid_player\n";
16196      kill  9, $pid_player;
16197      my $pid_mplayer = $pid_player + 1;
16198      kill  9, $pid_mplayer;
16199      $pid_player = undef;
16200      $playing_music = $false;
16201   }
16202
16203   draw_buttons(Draw_Play => $true); # play button ->
16204   $count = 0;
16205   $count_blink = 0;
16206
16207   if ( not $go_forward and not $go_back ){ # erase the color
16208      # To uncolor the selected artist, stopped artist line
16209      # color_the_selected_tracks_and_scroll( select_color => 'no_color', scroll => $false, set_rip => $false ); # default values
16210      color_the_selected_tracks_and_scroll( %args ) if $args{change_color};
16211   }
16212}
16213
16214# convert x seconds to hour:min:sec format
16215sub sec_to_time{
16216  my $i = number_value(shift);
16217  my ($hour,$min,$sec) = (0,0,0);
16218
16219  $min = sprintf("%02d", int($i/60) );  # 02d : leading zero
16220  my $remainder =  $i - $min * 60;      # $remainder =  $i % 60 ;; see <man perlop>
16221  $sec = sprintf("%02d", $remainder );
16222  #print "total_sec = $i --> hour = $hour ;; min = $min ;; sec = $sec\n";
16223  return ($hour,$min,$sec);
16224}
16225#sec_to_time('44500,893');
16226
16227# convert hour:min:sec format to  x seconds.
16228sub time_to_sec{    # $time = 2:34:01 or 01:06.39
16229  my $time = shift;
16230  my $sec  = 0;
16231  my $min  = 0;
16232  my $hour = 0;
16233
16234  if ($time =~ /(\d+):(\d+):(\d+[\.,]?\d*)/){
16235     $hour = $1;
16236     $min  = $2;
16237     $sec  = $3;
16238  }
16239  elsif ($time =~ /(\d+):(\d+[\.,]?\d*)/){
16240     $min  = $1;
16241     $sec  = $2;
16242  }
16243  elsif ($time =~ /(\d+[\.,]?\d*)/){
16244     $sec  = $1;
16245  }
16246  $sec = sprintf("%02d", number_value($sec) );   # 02.0f : leading zero
16247  #print "hour = $hour ;; min = $min ;; sec = $sec ;; time = $time";
16248  $time = int( $hour * 3600 + $min * 60 + $sec );
16249  #print " --> time = $time\n";
16250  return $time;
16251}
16252#time_to_sec ( "0:2,7456");
16253
16254sub rip_audio_cdparanoia { # MAX LENGTH = 230  for filename
16255        my $track = shift;
16256	my $file_wav = shift;
16257	my $row = $track -1;
16258	#Ripping from sector       0 (track  1 [0:00.00])
16259        #          to sector   18511 (track  1 [4:06.61])
16260
16261	my $opt = "";
16262	if ($button_noia1->get_active){ $opt .= "-Z ";}
16263	if ($button_noia2->get_active){ $opt .= "-z ";}
16264	if ($button_noia3->get_active){ $opt .= "-Y ";}
16265	if ($button_noia4->get_active){ $opt .= "-X ";}
16266
16267	my $cmd = "$nice_path -n $priority $cdparanoia_path -d $audiodevice_path -e -w $opt $track ";
16268
16269	my  $file_in   = $directory_output . '/' . $file_wav;
16270	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
16271
16272	insert_msg("." , "small-ini");
16273	insert_msg("$cmd \"$file_in2\" " , "small"); #print on debug textview
16274	insert_msg("\n" , "small");
16275
16276	# Use 'filename_from_unicode' to convert from unicode to local encoding.
16277	$file_in2  = filename_from_unicode $file_in2;
16278	_utf8_on($file_in2);  # Don't let Perl to convert $file_in2 to unicode. See 'man Encode'.
16279	$cmd .= " \"$file_in2\" ";
16280
16281	my $pid = open( NOIA, "$cmd  2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16282	$pid_rip_cda = $pid;
16283
16284	draw_input_output($da_arrow_dec,undef,("cda","wav"));
16285
16286	my $div = 0; # initial value
16287	my $saved_value = 0;
16288	while( <NOIA> ){
16289	        #print " fora $_\n";
16290	        # simple output:
16291		#outputting to track02.cdda.wav
16292		##: -2 [wrote] @ 682079
16293                ##: -2 [wrote] @ 683255
16294                ##: -2 [wrote] @ 684431
16295		#Ripping from sector       0 (track  1 [0:00.00])
16296                #          to sector   18511 (track  1 [4:06.61])
16297                # (== PROGRESS == [ >                            .| 019261 00 ] == :^D . ==)
16298
16299		if( $_ =~ /\[wrote\]\s+@\s+(\d+)/ )
16300		{
16301		  $saved_value = $div;
16302		  # progress = (sector-start)/(end-start);
16303		  # CD_FRAMESIZE_RAW   2352 /* bytes per frame, "raw" mode */:  1176 = (2352/2)
16304		  my $sector = $1/1176;  # sector divide by 1176
16305		  $div = sprintf("%0.2f", ( $sector - $start_sector[$row] )/$music_sector[$row] );
16306		  $div = number_value($div);
16307                  #                            show only not repeated value
16308		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ){
16309		     $pbar->set_fraction( $div ) ;
16310		     my $pct = sprintf("%2d", 100*$div );
16311		     $status_bar->push($context_id, $langs{msg054}." - $pct%");
16312		     #print ( " div  = $div ;; $1 / 1176 ;; start = $start_sector[$row] ;; music_sector[$row] = $music_sector[$row]\n");
16313		  }
16314		}
16315		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
16316		if ( not $normalize_button->get_active and $pid_rip_cda >= 0 ) {
16317		    $pid_rip_cda = StopProcessing($pid_rip_cda, $langs{msg055}, $file_wav );
16318		    exec_cmd_system2( 'rm', '-f', $file_in ); # remove wav file
16319		    close( NOIA );
16320		    return $false;
16321		}
16322	}
16323	close( NOIA );
16324	if ($normalize_button->get_active) {  # if normalize button is active
16325	    $pbar->set_fraction( 1 ) ;
16326	    $pid_rip_cda = -1; # reset rip pid on the final of process
16327	    return $true;
16328	}
16329}
16330
16331
16332sub rip_audio_cdda2wav {
16333        my $track = shift;
16334	my $file_wav = shift;
16335	my $row = $track-1;
16336
16337	my $opt = "";
16338	if ($button_cdda1->get_active){ $opt .= "-paranoia ";}
16339	if ($button_cdda2->get_active){ $opt .= "-paraopts=disable ";}
16340	if ($button_cdda3->get_active){ $opt .= "-paraopts=no-verify ";}
16341
16342	# cdda2wav -g -no-infofile   dev=/dev/hdc -t 2 track  - see cdda2wav -help or man cdda2wav
16343	my $cmd = "$nice_path -n $priority $cdda2wav_path dev=$audiodevice_path -x -g -no-infofile $opt -t $track ";
16344
16345	my  $file_in   = $directory_output . '/' . $file_wav;
16346	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
16347
16348	insert_msg("." , "small-ini");
16349	insert_msg("$cmd \"$file_in2\" " , "small"); #print on debug textview
16350	insert_msg("\n" , "small");
16351
16352	# Use 'filename_from_unicode' to convert from unicode to local encoding.
16353	$file_in2  = filename_from_unicode $file_in2;
16354	_utf8_on($file_in2);  # Don't let Perl to convert $file_in2 to unicode. See 'man Encode'.
16355	$cmd .= " \"$file_in2\" ";
16356
16357	my $pid = open( CDRIP, "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16358	$pid_rip_cda = $pid; ## set rip pid, pid > 0
16359
16360	my $div = 0; # initial value
16361	my $saved_value = 0;
16362
16363        ## loop for output - initial
16364	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
16365	while( <CDRIP> ){
16366	        # simple output:
16367		# 3%
16368
16369		if( $_ =~ /\s+(\d+)%/g )
16370		{
16371		  $saved_value = $div;
16372		  $div = sprintf("%0.2f", $1/100 );
16373		  $div = number_value($div);
16374		  #                            show only not repeated value
16375		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) {
16376		     $pbar->set_fraction( $div ) ;
16377		     $status_bar->push($context_id, " ripping audio to wav - $1%");
16378		     #print ( " div  = $div \n");
16379		  }
16380		}
16381		if( $_ =~ /100%.*successfully/i ){$pbar->set_fraction(1);}
16382
16383		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
16384		if ( not $normalize_button->get_active and $pid_rip_cda >= 0 ) {
16385		    $pid_rip_cda = StopProcessing($pid_rip_cda, " Stop ripping!", $file_wav);
16386		    exec_cmd_system2( 'rm', '-f', $file_in ); # remove wav file
16387		    $/="\n";  # abandon loop
16388		    close( CDRIP );
16389		    return $false;
16390		}
16391	}
16392	$/="\n"; ## final of loop
16393	close( CDRIP );
16394
16395	if ($normalize_button->get_active) {  # if normalize button is active
16396	    $pbar->set_fraction(1) ;
16397	    $pid_rip_cda = -1; # reset rip pid on the final of process
16398	    return $true;
16399	}
16400}
16401
16402##-----------------------------------------------------##
16403##-----------------------------------------------------##
16404
16405#  flac --> wav
16406sub decode_flac_to_wav {
16407   my %args = ( @_ );
16408   my $file_wav = $args{file_wav};  my $filepath = $args{filepath};
16409
16410        my  $file_in   = $filepath;
16411	my  $file_out  = $directory_output . '/' . $file_wav;
16412	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
16413	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
16414	print "decode_flac_to_wav --> Could not find flac file: $file_in\n" if( ! -e filename_from_unicode $file_in );
16415
16416	my $cmd = "$nice_path -n $priority  $flac_path -d \"$file_in2\" -f -o \"$file_out2\" ";
16417
16418	#print "\n$cmd\n";
16419	insert_msg("." , "small-ini");
16420	insert_msg("$cmd" , "small"); #print on debug textview
16421	insert_msg("\n" , "small");
16422
16423	my $pid = open( FLACDEC, filename_from_unicode "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16424	$pid_flac_decode = $pid; ## set flac pid, pid > 0
16425
16426	my $div = 0; # initial value
16427	my $saved_value = 0;
16428
16429	## loop for output - initial
16430	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
16431	while( <FLACDEC> )
16432	{
16433	        #output:
16434		# 10-Que � feito de voc�.flac: 75% complete
16435
16436		if( $_ =~ /.*:\s+(\d+)%\scomplete/ )
16437		{
16438		  #print "\n<<>> $_ --->1=$1 \n";
16439		  $saved_value = $div;
16440		  $div = sprintf("%0.2f", $1/100 );
16441		  $div = number_value($div);
16442		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
16443
16444                  #                            show only not repeated value
16445		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) {
16446		     $pbar->set_fraction( $div ) ;
16447		     $status_bar->push($context_id, " flac decoding $1%");
16448		     #print ( " div  = $div \n");
16449		  }
16450		}
16451		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
16452		if ( not $normalize_button->get_active and $pid_flac_decode >= 0 ) {
16453		    $pid_flac_decode = StopProcessing($pid_flac_decode, " Stop flac decoding!", $file_wav);
16454		    exec_cmd_system2( 'rm', '-f', $file_out ); # remove wav file
16455		    $/="\n";  # abandon loop
16456		    close( FLACDEC );
16457		    return $false;
16458		}
16459	}
16460	$/="\n"; ## final of loop
16461	close( FLACDEC );
16462
16463	if ($normalize_button->get_active ) {  # if normalize button is active
16464	    $pbar->set_fraction( 1 ) ;
16465	    $pid_flac_decode = -1;       # reset ape deocde pid on the final of process
16466	    return $true;
16467	}
16468}
16469
16470
16471#  mp4 --> wav
16472sub decode_mp4_to_wav {
16473   my %args = ( @_ );
16474   my $file_wav = $args{file_wav};  my $filepath = $args{filepath};
16475
16476        my  $file_in   = $filepath;
16477	my  $file_out  = $directory_output . '/' . $file_wav;
16478	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
16479	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
16480	print "decode_mp4_to_wav --> Could not find mp4 file: $file_in\n" if( ! -e filename_from_unicode $file_in );
16481
16482	my $cmd = "$nice_path -n $priority $faad_path \"$file_in2\" -f 1 -o \"$file_out2\" ";
16483
16484	insert_msg("." , "small-ini");
16485	insert_msg("$cmd" , "small"); #print on debug textview
16486	insert_msg("\n" , "small");
16487
16488	my $pid = open( FAADDEC, filename_from_unicode "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16489	my $pid_faad_decode = $pid; ## set flac pid, pid > 0
16490
16491	my $div = 0; # initial value
16492	my $saved_value = 0;
16493
16494	$/="\r";
16495	while( <FAADDEC> )
16496	{
16497	        #output:
16498		# 45% decoding music.mp4.
16499
16500		if( $_ =~ /^(\d+)%\s+decoding/ )
16501		{
16502		  $saved_value = $div;
16503		  $div = sprintf("%0.2f", $1/100 );
16504		  $div = number_value($div);
16505		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
16506
16507                  #                            show only not repeated value
16508		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) {
16509		     $pbar->set_fraction( $div ) ;
16510		     $status_bar->push($context_id, " faad decoding $1%");
16511		  }
16512		}
16513		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
16514		if ( not $normalize_button->get_active and $pid_faad_decode >= 0 ) {
16515		    $pid_faad_decode = StopProcessing($pid_faad_decode," Stop faad decoding!", $file_wav);
16516		    exec_cmd_system2( 'rm', '-f', $file_out ); # remove wav file
16517		    $/="\n";  # abandon loop
16518		    close( FAADDEC );
16519		    return $false;
16520		}
16521	}
16522	$/="\n"; ## final of loop
16523	close( FAADDEC );
16524
16525	if ($normalize_button->get_active ) {  # if normalize button is active
16526	    $pbar->set_fraction( 1 ) ;
16527	    $pid_faad_decode = -1;       # reset ape deocde pid on the final of process
16528	    return $true;
16529	}
16530}
16531
16532
16533#  ape --> wav  # dependence: mac from APE-3.96b8-3plf-MonkeyAudio.i586.rpm
16534sub decode_ape_to_wav {
16535   my %args = ( @_ );
16536   my $file_wav = $args{file_wav};  my $filepath = $args{filepath};
16537
16538        my  $file_in   = $filepath;
16539	my  $file_out  = $directory_output . '/' . $file_wav;
16540	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
16541	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
16542	print "decode_ape_to_wav --> Could not find ape file: $file_in\n" if( ! -e filename_from_unicode $file_in );
16543
16544	my $cmd = "$nice_path -n $priority $ape_path  \"$file_in2\" \"$file_out2\" -d ";
16545
16546	#print "\n$cmd\n";
16547	insert_msg("." , "small-ini");
16548	insert_msg("$cmd" , "small"); #print on debug textview
16549	insert_msg("\n" , "small");
16550
16551	# STDIN is file descriptor number 0; STDOUT number 1; and STDERR number 2.
16552	# To exchange a command's STDOUT and STDERR, i.e., capture the STDERR
16553	# but have its STDOUT come out on our old STDERR:  3>&1 1>&2 2>&3 3>&-
16554	# 3>&1 : Make a new file descriptor, number 3, be a copy of number 1.
16555	# d3=d1
16556	# d1=d2
16557	# d2=d3
16558	my $pid = open( APEDEC, filename_from_unicode "$cmd 3>&1 1>&2 2>&3 3>&- |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16559	$pid_ape = $pid; ## set ape pid, pid > 0
16560
16561	my $div = 0; # initial value
16562	my $saved_value = 0;
16563
16564	## loop for output - initial
16565	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
16566	while( <APEDEC> )
16567	{
16568	        #output:
16569		#--- Monkey's Audio Console Front End (v3.96b8) (c) Matthew T. Ashland ---
16570                #Decompressing ...
16571                #Progress:  3.0% (357.7 sec remaining, 368.8 sec total)
16572
16573		if( $_ =~ /^Progress:\s+(\d+[\.,]\d)%\s\((.*remaining).*/i )
16574		{
16575		  #print "\n<<>> $_ --->1=$1  --->2=$2\n";
16576		  $saved_value = $div;
16577		  $div = sprintf("%0.2f", $1/100 );
16578		  $div = number_value($div);
16579		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
16580
16581                  #                            show only not repeated value
16582		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) {
16583		  $pbar->set_fraction( $div ) ;
16584		  $status_bar->push($context_id, " ape decoding $1% | $2");
16585		  #print ( " div  = $div \n");
16586		  }
16587		}
16588		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
16589		if ( not $normalize_button->get_active and $pid_ape >= 0 ) {
16590		    $pid_ape = StopProcessing($pid_ape," Stop ape decoding!", $file_wav);
16591		    exec_cmd_system2( 'rm', '-f', $file_out ); # remove wav file
16592		    $/="\n";  # abandon loop
16593		    close( APEDEC );
16594		    return $false;
16595		}
16596	}
16597	$/="\n"; ## final of loop
16598	close( APEDEC );
16599
16600	if ($normalize_button->get_active ) {  # if normalize button is active
16601	    $pbar->set_fraction( 1 ) ;
16602	    $pid_ape = -1;        # reset ape deocde pid on the final of process
16603	    return $true;
16604	}
16605}
16606
16607#  mpc --> wav
16608sub decode_mpc_to_wav {
16609   my %args = ( @_ );
16610   my $file_wav = $args{file_wav};  my $filepath = $args{filepath};
16611
16612        my  $file_in   = $filepath;
16613	my  $file_out  = $directory_output . '/' . $file_wav;
16614	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
16615	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
16616	print "decode_mpc_to_wav --> Could not find mpc file: $file_in\n" if( ! -e filename_from_unicode $file_in );
16617
16618	my $cmd = "$nice_path -n $priority $mpcdec_path  \"$file_in2\" --wav \"$file_out2\" ";
16619
16620	#print "\n$cmd\n";
16621	insert_msg("." , "small-ini");
16622	insert_msg("$cmd" , "small"); #print on debug textview
16623	insert_msg("\n" , "small");
16624
16625	## $res = pid of ogg
16626	my $res = open( MPCDEC, filename_from_unicode "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16627	$pid_mpc_decode = $res; ## set ogg pid, pid > 0
16628
16629	my $div = 0; # initial value
16630	my $saved_value = 0;
16631
16632	## loop for output - initial
16633	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
16634	while( <MPCDEC> )
16635	{
16636	        #output:
16637		# 203.6 kbps,    4:52.68, SV 7.0, Profile 'BrainDead' (Beta 1.14)
16638                #     1:20.58/    4:52.68 decoded (27.5%)
16639
16640		if( $_ =~ /\sdecoded\s\((\s*\d*[\.,]\d)%\)/ )
16641		{
16642		  $saved_value = $div;
16643		  $div = sprintf("%0.2f", $1/100 );
16644		  $div = number_value($div);
16645		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
16646
16647                  #                            show only not repeated value
16648		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) {
16649		  $pbar->set_fraction( $div ) ;
16650		  $status_bar->push($context_id, " mpc decode $1%");
16651		  #print ( " div  = $div \n");
16652		  }
16653		}
16654		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
16655
16656		if ( not $normalize_button->get_active and $pid_mpc_decode >= 0 ) {
16657	            $pid_mpc_decode = StopProcessing($pid_mpc_decode, " Stop mpc decoding!", $file_wav);
16658	            if ( -e filename_from_unicode $file_out ){
16659		       exec_cmd_system2( 'rm', '-f', $file_out ); # remove wav file
16660		    }
16661		    close( MPCDEC );
16662		    return $false;
16663		}
16664	}
16665	$/="\n"; ## final of loop
16666	close( MPCDEC );
16667
16668	if ($normalize_button->get_active ) {  # if normalize button is active
16669	    $pbar->set_fraction( 1 ) ;
16670	    $status_bar->push($context_id, " mpc decode 100%");
16671	    $pid_mpc_decode = -1;       # reset oggdec pid on the final of process
16672	    return $true;
16673	}
16674}
16675
16676#  ogg --> wav
16677
16678sub decode_ogg_to_wav {
16679   my %args = ( @_ );
16680   my $file_wav = $args{file_wav};  my $filepath = $args{filepath};
16681
16682	my  $file_in   = $filepath;
16683	my  $file_out  = $directory_output . '/' . $file_wav;
16684	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
16685	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
16686	print "decode_ogg_to_wav --> Could not find ogg file: $file_in\n" if( ! -e filename_from_unicode $file_in );
16687
16688	my $cmd = "$nice_path -n $priority $oggdec_path  \"$file_in2\" -o \"$file_out2\" ";
16689
16690	#print "\n$cmd\n";
16691	insert_msg("." , "small-ini");
16692	insert_msg("$cmd" , "small"); #print on debug textview
16693	insert_msg("\n" , "small");
16694
16695	my $res = open( OGGDEC, filename_from_unicode "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16696	$pid_ogg_decode = $res; ## set ogg pid, pid > 0
16697
16698	my $div = 0; # initial value
16699	my $saved_value = 0;
16700
16701	## loop for output - initial
16702	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
16703	while( <OGGDEC> )
16704	{
16705	        #output:
16706		# OggDec 1.0
16707                # Decoding "lix�o-��o ��t�es'a.ogg" to "lix�o-��o ��t�es'a.wav"
16708		#          [  5.5%]
16709		#          [ 88.5%]
16710                #          [100.0%]
16711
16712		if( $_ =~ /^\s*\[(\s*\d*)[\.,](\d)%\]/ )
16713		{
16714		  $saved_value = $div;
16715		  $div = sprintf("%0.2f", $1/100 );
16716		  $div = number_value($div);
16717		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
16718
16719                  #                            show only not repeated value
16720		  if ( $pbar->get_fraction() <=1 and $div > $saved_value ) {
16721		  $pbar->set_fraction( $div ) ;
16722		  $status_bar->push($context_id, " ogg decode $1%");
16723		  }
16724		}
16725		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
16726
16727		if ( not $normalize_button->get_active and $pid_ogg_decode >= 0 ) {
16728	            $pid_ogg_decode = StopProcessing($pid_ogg_decode, " Stop ogg decoding!", $file_wav);
16729		    if ( -e filename_from_unicode $file_out ){
16730		       exec_cmd_system2( 'rm', '-f', $file_out ); # remove wav file
16731		    }
16732		    close( OGGDEC );
16733		    return $false;
16734		}
16735	}
16736	$/="\n"; ## final of loop
16737	close( OGGDEC );
16738
16739	if ($normalize_button->get_active) {  # if normalize button is active
16740	    $pid_ogg_decode = -1;             # reset oggdec pid on the final of process
16741	    return $true;
16742	}
16743}
16744
16745
16746#  mp3 --> wav
16747
16748sub decode_mp3_to_wav {
16749   my %args = ( @_ );
16750   my $file_mp3 = $args{file_mp3};   my $file_wav = $args{file_wav};  my $filepath = $args{filepath};
16751
16752        my $cmd = "$nice_path -n $priority $lame_path --verbose --decode ";
16753
16754	my  $file_in   = $filepath;
16755	my  $file_out  = $directory_output . '/' . $file_wav;
16756	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
16757	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
16758	print "decode_mp3_to_wav --> Could not find mp3 file: $file_in\n" if( ! -e filename_from_unicode $file_in );
16759
16760	$cmd .= "\"$file_in2\" \"$file_out2\"";
16761
16762	insert_msg("." , "small-ini");
16763	insert_msg("$cmd" , "small"); #print on debug textview
16764	insert_msg("\n" , "small");
16765
16766	my $pid = open( LAME, filename_from_unicode "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16767	$pid_lame_decode = $pid; ## set lame pid, pid > 0  see <man perlipc>
16768
16769	my $saved_value = 0;
16770	my $div = 0;
16771
16772	## loop for output - initial
16773	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
16774	while( <LAME> )
16775	{
16776	        #output:
16777                #ID3v2 found. Be aware that the ID3 tag is currently lost when transcoding.
16778                #input:  almir_sater-instrumental-01_corumb�.mp3
16779                #        (44.1 kHz, 2 channels, MPEG-1 Layer III)
16780                #output: almir_sater-instrumental-01_corumb�.mp3.wav  (16 bit, Microsoft WAVE)
16781                #skipping initial 1105 samples (encoder+decoder delay)
16782                #Frame#  2806/8937   160 kbps  L  R
16783
16784		#print (" $_ \n");
16785
16786		if( $_ =~ /^\s*Frame\#\s+(\d+)\/(\d+)\s+.*/ )
16787		{
16788		   my $frames = $1;
16789	           my $numframes = $2;
16790		   if ( $2 == 0 ){return;}
16791		   $saved_value = $div;
16792		   $div = sprintf("%0.2f", $1/$2 );
16793		   $div = number_value($div);
16794		   my $pct = sprintf("%2.0f", $div*100 );
16795		   if ( $div > 1){ $div = 1;}
16796		   #next if ($div <= $saved_value);
16797
16798		   if ( $pbar->get_fraction() <=1 and $div > $saved_value ) {
16799		      $pbar->set_fraction( $div ) ;
16800		      $status_bar->push($context_id, " lame decode $pct%");
16801		   }
16802		}
16803		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
16804
16805		if ( not $normalize_button->get_active and $pid_lame_decode >= 0 ) {
16806		    # set lamepid to -1 after stop the process
16807	            $pid_lame_decode = StopProcessing($pid_lame_decode, $langs{msg057}, $file_wav );
16808		    # In the case of stop decode process, always remove file_wav if it exist
16809	            # because the file_wav is not complete/entire
16810		    if ( -e filename_from_unicode $file_out ){
16811		       exec_cmd_system2( 'rm', '-f', $file_out ); # remove wav file
16812		    }
16813		    close( LAME );
16814		    return $false;
16815		}
16816	}
16817	$/="\n"; ## final of loop
16818	close( LAME );
16819
16820	if ($normalize_button->get_active) {  # if normalize button is active
16821	    $pid_lame_decode = -1;            # reset lame pid on the final of process
16822	    return $true;
16823	}
16824}
16825
16826sub normalize_wav { #29Abr2008 -- normalize_wav_with_wavegain
16827   my %args = ( @_ );
16828   my $dir = $args{directory};   my $file_wav = $args{file_wav};  my $filepath = $args{filepath};
16829   my $row = $args{row};
16830
16831   $adjust = 0;
16832   $already_normalized = $false;
16833
16834   if ( not -e filename_from_unicode "$directory_output/$file_wav" ) {
16835      insert_msg("\n$directory_output/$file_wav : $! \n" , "small-red"); # print on debug textview
16836      $normalize_button->set_active($false);
16837      return $false;
16838   }
16839
16840   make_directory_final(%args);
16841
16842   # -12.0dB <= $gain <= +12.0dB ; $gain = 0.0dB correspond to 89dB standard value
16843   $gain = sprintf("%0.2f", $spinner->get_value );
16844   $gain = number_value($gain);  # For example, change '0,57' by '0.57'.
16845
16846   my $Coment = $entry_def_comment->get_text;
16847   $Coment = remove_change_10_chars($Coment);
16848
16849   if ( $Coment ne "" ) {
16850
16851      my $gain_com = $gain;
16852      $gain_com = 'Not Normalized' if $norm_type eq 'None';
16853      $gain_com = '0.00'           if $norm_type eq 'Album';
16854
16855      $Coment =~ s/\%gain/$gain_com/ig;
16856      $files_info[$row]{comment} = $Coment;  # change comment tag value
16857   }
16858
16859   # get the wave total miliseconds ;;
16860   # 2352 = bytes per frame, "raw" mode ;; 1 sec have 75 frames = 75 * 2352 bytes = 176400 bytes
16861   my $fileSize = -s filename_from_unicode "$directory_output/$file_wav"; # $fileSize = $FileLength + 8 = Wav DataLength + 44
16862   $metadata{'Length'} = int( ($fileSize - 44) / 176400 * 1000 ) ;        # length of audio file in milliseconds
16863   #print " Length = $metadata{'Length'} ;; fileSize = $fileSize bytes\n";
16864
16865   my @cmd_args = ( $nice_path, '-n', $priority, $wavegain_path, '-r', '-y' );
16866
16867   if    ( $norm_type eq "Track" ) { push @cmd_args, ( '-g', $gain  );   } # add more args to command
16868   elsif ( $norm_type eq "Album" ) { push @cmd_args, ( '-g', '0.00' );   } # don't implemented yet!
16869   elsif ( $norm_type eq "None"  ) {  #----------------------------------#
16870      $pbar_n->set_text( $langs{msg045} );                               #
16871      insert_msg( $langs{msg041} , "small-red");                         #
16872      insert_msg("\n" , "small");                                        #
16873      $pid_normalize = -1;                                               #
16874      return;                                                            #
16875   }#--------------------------------------------------------------------#
16876
16877   #my $cmd = "$wavegain_path -r -y -g $gain \"$directory_output/$file_wav\" ";
16878   my $cmd  = "@cmd_args ";
16879
16880   my $file_in   = $directory_output . '/' . $file_wav;
16881   (my $file_in2 = $file_in) =~ s/"/\\"/g;  # change all " characters by \"
16882   print "normalize_wav --> Could not find wav file: $file_in\n" if( ! -e filename_from_unicode $file_in );
16883
16884   $cmd .= " \"$file_in2\"";
16885
16886   insert_msg("." , "small-ini");
16887   insert_msg("$cmd" , "small"); #print on debug textview
16888   insert_msg("\n" , "small");
16889
16890   my $div = 0;
16891   my $saved_value = 0;
16892
16893   $pbar_n->set_text( $langs{msg006} ); # "Analyzing with ReplayGain normalization algorithms ..."
16894   while (Gtk2->events_pending()) { Gtk2->main_iteration()};
16895
16896   # see 'man perlfaq8', 'man IPC::Open3' or 'fttp://perldoc.perl.org'.
16897   # use open3 because it entries ('command', 'args') is Locale-independent (don't have conversion '0.30' to '0,30').
16898   # use IPC::Open3;
16899   # use Symbol qw(gensym);
16900
16901   # Let STDOUT go to our own STDERR and capture a program's STDERR:
16902   # $pid = open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR,'some cmd and args', 'optarg', ...);
16903   # my $pid = open3(gensym, ">&STDERR", \*NORMALIZE, @cmd_args) or die "Couldn't execute \"@cmd_args\": $!, stopped\n";
16904
16905   my $pid = open( NORMALIZE, filename_from_unicode "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
16906   $pid_normalize = $pid; # set normalize pid
16907
16908   $/="\r"; ## loop for output - initial
16909   while( <NORMALIZE> )
16910   {
16911      # Analyzing...
16912
16913      #    Gain   |  Peak  | Scale | New Peak |Left DC|Right DC| Track
16914      #           |        |       |          |Offset | Offset | Track
16915      # --------------------------------------------------------------
16916      #  +6.91 dB |  14245 |  2.22 |    31561 |    0  |     0  | 05-Jo�o Bosco-Escadas da Penha.wav
16917
16918      # Applying Gain of +6.91 dB to file: 05-Jo�o Bosco-Escadas da Penha.wav
16919      #This file 100% done     All files  99% done
16920      # WaveGain Processing completed normally
16921
16922      # No Title Gain adjustment or DC Offset correction required for file:
16923
16924      # Error renaming '/tmp/wavegain.tmp' to '04.wav' (uh-oh)
16925      # Error processing GAIN for file - 04.wav
16926
16927      #print "--> $_\n";
16928
16929      if ( $_ =~ /^\s+Applying Gain of ([-+]\d+[.,]\d+) dB to file:/ ){ $adjust = $1; }
16930
16931      if ( $_ =~ /^This\sfile\s+(\d+)\%\s*done\s+All\sfiles/ )
16932      {
16933         $saved_value = $div;
16934         $div = sprintf("%0.2f", $1/100 );
16935         $div = number_value($div);
16936	 if ( $div >= 1 ){ $div = 1;}  # to avoid possible errors
16937
16938	 #                            show only not repeated value
16939         if ( $pbar_encode->get_fraction() <=1 and $div > $saved_value ) {
16940	    $pbar_n->set_fraction( $div ) ;
16941	    $pbar_n->set_text( $langs{msg050} . "${adjust}dB...");
16942	    $status_bar->push($context_id, $langs{msg051} . "${adjust}dB");
16943	    $pbar_n->set_orientation('left_to_right');
16944         }
16945      }
16946      if ( $_ =~ /WaveGain Processing completed normally/ )
16947      {
16948         $pbar_n->set_fraction( 1 ) ;
16949	 $pbar_n->set_text( $langs{msg050} . "${adjust}dB...");
16950	 $status_bar->push($context_id, $langs{msg051} . "${adjust}dB");
16951      }
16952      #if ( $_ =~ /Error renaming/){ insert_msg("$_" , "small-red"); insert_msg("\n" , "small"); }
16953      if ( $_ =~ /No Title Gain adjustment.*required/ ){
16954         $already_normalized = $true;
16955         $pbar_n->set_fraction( 1 ) ;
16956         $pbar_n->set_text( $langs{msg047} );
16957         insert_msg( $langs{msg048} . "\n", "small-red");
16958      }
16959      while (Gtk2->events_pending()) { Gtk2->main_iteration()};
16960
16961      if ( not $normalize_button->get_active and $pid_normalize >= 0 ) {
16962         $pid_normalize = StopProcessing($pid_normalize, $langs{msg049}, $file_wav );
16963         $/="\n";  # abandon loop
16964      }
16965   }
16966   $/="\n"; ## final of loop
16967   close( NORMALIZE );
16968   waitpid($pid, 0);
16969
16970   if ($normalize_button->get_active) {  # if normalize button is active
16971      if ( not $already_normalized ){
16972         insert_msg( $langs{msg053}."${adjust}dB." , "black");
16973	 insert_msg("\n" , "small");
16974      }
16975      $pid_normalize = -1;  # reset normalize pid on the final of process
16976      return $true;
16977   }
16978   else {return $false;}
16979}
16980
16981sub encode_wav_to_wav {  # actualy, don't need to encode because the output is wav!
16982   my %args = ( @_ );
16983   my $row = $args{row}; my $file_wav = $args{file_wav};
16984
16985   my $file_in  = $directory_output.'/'.$file_wav;
16986   my $file_out = $directory_final.'/'.$file_wav;
16987
16988   remove_apetag (%args, (filepath => $file_in) );
16989   $metadata{'Encoded by'} = "WAVEGAIN with gnormalize (http://gnormalize.sourceforge.net)";
16990   write_apetag  (%args, (filepath => $file_in) );
16991
16992   my @cmd = ( 'cp', '-f', $file_in, $file_out );
16993   insert_msg("." , "small-ini");
16994   insert_msg("@cmd" , "small");      # print on debug textview
16995   insert_msg("\n" , "small");
16996   exec_cmd_system2(@cmd);
16997
16998   if   ($normalize_button->get_active) { return $true; }
16999   else { return $false;}
17000}
17001
17002# wav --> ogg
17003sub encode_wav_to_ogg {
17004   my %args = ( @_ );
17005   my $row = $args{row}; my $file_ogg = $args{file_ogg}; my $file_wav = $args{file_wav}; my $filepath = $args{filepath};
17006
17007   my $Title   = $files_info[$row]{title};
17008   my $Artist  = $files_info[$row]{artist};
17009   my $Comment = $files_info[$row]{comment};
17010   my $Album   = $files_info[$row]{album};
17011   my $Genre   = $files_info[$row]{genre};
17012   my $Year    = $files_info[$row]{year};
17013   my $Track_Number = $files_info[$row]{track};
17014   my $Total_Track  = $files_info[$row]{total_track};
17015
17016	my $bitrate;
17017	my $mode;
17018	my $freq;
17019
17020        ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max) = encode_settings(%args);
17021
17022	# bug of oggenc:
17023        # The command, for example, "oggenc -q 5.5 -m 96 -M 320 music.wav" has a bug for options
17024        # -q, -m, and -M, such that -m is minimum allowed bitrate and -M is maximum allowed bitrate.
17025        # The solution is omit the options -m and -M, so the corrected command is
17026        # "oggenc -q 5.5 music.wav" used for variable bitrate.
17027
17028	my $cmd = "$nice_path -n $priority  $oggenc_path ";
17029
17030	# oggenc commands
17031	if ( $encode eq "variable" ){ # VBR (variable bitrate)
17032	   # m - minimum allowed bitrate - see  <man oggenc>
17033	   # M - maximum allowed bitrate
17034	   # q - Variable  BitRate -  -1 <= quality <= 10
17035	   # min and max: -m $vb_Min -M $vb_Max
17036	   # $cmd = "$oggenc_path -q $Vogg -m $vb_Min -M $vb_Max "; # bug of oggenc. Don't use -m and -M options
17037	   $cmd .= "-q $Vogg ";
17038        }
17039	else{  # for ABR (average bitrate)
17040	   $cmd .= "-b $bitrate -m $vb_Min -M $vb_Max ";
17041	}
17042	$additional_command_ogg = $entry_command_ogg->get_text;
17043	$cmd .= " --resample \"$freq\" $additional_command_ogg ";
17044
17045	my  $file_in   = $directory_output . '/' . $file_wav;
17046	my  $file_out  = $directory_final  . '/' . $file_ogg;
17047	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
17048	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
17049	print "encode_wav_to_ogg --> Could not find wave file: $file_in\n" if( ! -e filename_from_unicode $file_in );
17050
17051	$cmd .= " \"$file_in2\" -o \"$file_out2\" ";
17052
17053	insert_msg("." , "small-ini");
17054	insert_msg("$cmd" , "small"); #print on debug textview
17055	insert_msg("\n" , "small");
17056
17057	## start encoding
17058	my $res = open( OGGENC, filename_from_unicode "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
17059	$pid_ogg_encode = $res; ## set ogg encode pid
17060
17061	my $saved_value = 0;
17062	my $div = 0;
17063
17064	## loop for output - initial
17065	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
17066	while( <OGGENC> )
17067	{
17068		## this is output of oggenc:
17069
17070		# Opening with wav module: WAV file reader
17071                # Encoding "music.wav" to
17072                #          "music.ogg"
17073                # at quality 3.00
17074		#        [  1.5%] [ 0m13s remaining] |
17075                #        [ 21.6%] [ 0m08s remaining] /
17076		#        [ 99.9%] [ 0m00s remaining] -
17077		# Done encoding file "music.ogg"
17078                #        File length:  0m 56.0s
17079                #        Elapsed time: 0m 10.7s
17080                #        Rate:         5.3075
17081                #        Average bitrate: 114.4 Kb/s
17082
17083		if( $_ =~ /^\s*\[(\s*\d*[\.,]\d)%\]\s+\[\s*(\d*)m(\d*)s\sremaining\]/ )
17084		{
17085		  $saved_value = $div;
17086		  $div = sprintf("%0.2f", number_value($1)/100 );
17087		  $div = number_value($div);
17088		  my $pct = sprintf("%02d", number_value($1) ); # 02d : leading zero
17089		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
17090
17091                  #                            show only not repeated value
17092		  if ( $pbar_encode->get_fraction() <=1 and $div > $saved_value ) {
17093		  $pbar_encode->set_fraction( $div ) ;
17094		  $status_bar->push($context_id, " ogg encode $pct% | time remaining = $2:$3");
17095		  }
17096		}
17097		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
17098
17099		if ( not $normalize_button->get_active and $pid_ogg_encode >= 0 ) {
17100		    $pid_ogg_encode = StopProcessing($pid_ogg_encode," Stop ogg encoding!", $file_wav);
17101		    $/="\n";  # abandon loop
17102		}
17103	}
17104	$/="\n"; ## final of loop
17105	close(OGGENC);
17106
17107	my   @cmd_tag = ( $vorbiscomment_path, '--raw', '-t', "title=$Title", '-t', "artist=$Artist" );
17108        push @cmd_tag,  ( '-t', "genre=$Genre", '-t', "date=$Year", '-t', "album=$Album" );
17109        if ( $Total_Track ne "" and $Total_Track >= $Track_Number ) {
17110               push @cmd_tag, ( '-t', "tracknumber=$Track_Number/$Total_Track" ); }
17111        else { push @cmd_tag, ( '-t', "tracknumber=$Track_Number" );              }
17112	push @cmd_tag,  ( '-t', "description=$Comment", '-t', "comment=$Comment", '-w' );
17113
17114        if ( $vorbiscomment_path ne "" and $normalize_button->get_active ){
17115	   insert_msg("." , "small-ini");
17116	   insert_msg("@cmd_tag \"$file_out2\"" , "small"); #print on debug textview
17117	   insert_msg("\n" , "small");
17118
17119	   push @cmd_tag, ( filename_from_unicode $file_out );
17120	   exec_cmd_system3(@cmd_tag);
17121	}
17122
17123	if ($normalize_button->get_active) {  # if normalize button is active
17124	    $pid_ogg_encode = -1;             # reset pid on the final of process
17125            return $true;    }
17126	else { return $false;}
17127}
17128
17129
17130# wav --> mpc
17131sub encode_wav_to_mpc {
17132   my %args = ( @_ );
17133   my $row = $args{row}; my $file_mpc = $args{file_mpc}; my $file_wav = $args{file_wav}; my $filepath = $args{filepath};
17134
17135   my $Title   = $files_info[$row]{title};
17136   my $Artist  = $files_info[$row]{artist};
17137   my $Comment = $files_info[$row]{comment};
17138   my $Album   = $files_info[$row]{album};
17139   my $Genre   = $files_info[$row]{genre};
17140   my $Year    = $files_info[$row]{year};
17141   my $Track_Number = $files_info[$row]{track};
17142   my $Total_Track  = $files_info[$row]{total_track};
17143
17144	my $bitrate;
17145	my $mode;
17146	my $freq;
17147
17148        ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max) = encode_settings(%args);
17149
17150	# --nmt x     set NMT value to x dB (dflt:  6.5)
17151	# smr (signal to mask ratio)
17152	# --minSMR over 0 db will result in full bandwidth encoding.
17153	#$cmd = "$mpcenc_path --overwrite --quality $Vmpc --minSMR 0 --nmt 12 --tmn 32 ";
17154	my $profile = '--standard';
17155	if ( $Vmpc < 2.5 ){ $profile = '--telephone'; }
17156	elsif ( $Vmpc < 3.5 ){ $profile = '--thumb';  }
17157	elsif ( $Vmpc < 4.5 ){ $profile = '--radio';  }
17158	elsif ( $Vmpc < 5.5 ){ $profile = '--standard'; }
17159	elsif ( $Vmpc < 6.5 ){ $profile = '--xtreme'; }
17160	elsif ( $Vmpc < 7.5 ){ $profile = '--insane'; }
17161	else { $profile = '--braindead'; }
17162
17163	$additional_command_mpc = $entry_command_mpc->get_text;
17164
17165	my $cmd = "$nice_path -n $priority  $mpcenc_path --overwrite $profile $additional_command_mpc ";
17166
17167	my  $file_in   = $directory_output . '/' . $file_wav;
17168	my  $file_out  = $directory_final  . '/' . $file_mpc;
17169	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
17170	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
17171	print "encode_wav_to_mpc --> Could not find wave file: $file_in\n" if( ! -e filename_from_unicode $file_in );
17172
17173	insert_msg("." , "small-ini");
17174	insert_msg("$cmd \"$file_in2\" \"$file_out2\" " , "small"); #print on debug textview
17175	insert_msg("\n" , "small");
17176
17177	# Use 'filename_from_unicode' to convert from unicode to local encoding.
17178	$file_in2  = filename_from_unicode $file_in2;
17179	$file_out2 = filename_from_unicode $file_out2;
17180	_utf8_on($file_in2);  # Don't let Perl to convert $file_in2 to unicode. See 'man Encode'.
17181	_utf8_on($file_out2); # Turns on the UTF8 flag in $file_out2 string.
17182	$cmd .= " \"$file_in2\" \"$file_out2\" ";
17183
17184	# start encoding
17185	my $res = open( MPCENC, "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
17186	$pid_mpc_encode = $res; # set mpc encode pid
17187
17188	my $saved_value = 0;
17189	my $div = 0;
17190
17191	## loop for output - initial
17192	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
17193	while( <MPCENC> )
17194	{
17195		## this is output of mppenc:
17196		#    %|avg.bitrate| speed|play time (proc/tot)| CPU time (proc/tot)| ETA
17197                #  5.9  119.8 kbps  7.67x     0:17.1    4:52.6     0:02.2    0:38.1     0:35.9
17198		# 24.4  126.3 kbps  7.49x     1:11.5    4:52.6     0:09.5    0:39.0     0:29.5
17199                # 24,4
17200
17201		# print "aquiiii: $_\n";
17202
17203		if( $_ =~ /^\s+(\d+[\.,]\d+)\s+.*kbps\s+(\d+[\.,]\d+)x.*(\d+:\d+)/ )
17204		{
17205		  #print "\n<<>> $_ --->1=$1  --->2=$2  --->3=$3\n";
17206		  $saved_value = $div;
17207		  $div = sprintf("%0.2f", $1/100 );
17208		  $div = number_value($div);
17209		  my $speed = sprintf("%0.1f", $2 );
17210		  my $pct = sprintf("%02d", $1 ); # 02d : leading zero
17211		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
17212
17213                  #                            show only not repeated value
17214		  if ( $pbar_encode->get_fraction() <=1 and $div > $saved_value ) {
17215		  $pbar_encode->set_fraction( $div ) ;
17216		  # print ( " div  = $div  ; time remain = $3 \n");
17217		  $status_bar->push($context_id, " mpc encode $pct% | time remaining = $3 | encode speed = $speed");
17218		  }
17219		}
17220		while (Gtk2->events_pending()) { Gtk2->main_iteration()};
17221
17222		if ( not $normalize_button->get_active and $pid_mpc_encode >= 0 ) {
17223		    $pid_mpc_encode = StopProcessing($pid_mpc_encode," Stop mpc encoding!", $file_wav);
17224		    $/="\n";  # abandon loop
17225		}
17226	}
17227	$/="\n"; ## final of loop
17228	close(MPCENC);
17229
17230        if( -e filename_from_unicode $file_out ) {
17231
17232	   remove_apetag (%args, (filepath => $file_out) );
17233	   $metadata{'Encoded by'} = "MPCENC with gnormalize (http://gnormalize.sourceforge.net)";
17234	   write_apetag  (%args, (filepath => $file_out) );
17235        }
17236        else { print "encode_wav_to_mpc --> Could not find mpc file: $file_out\n"; }
17237
17238	if ($normalize_button->get_active) {  # if normalize button is active
17239	    $pid_mpc_encode = -1;             # reset pid on the final of process
17240            return $true;    }
17241	else { return $false;}
17242}
17243
17244
17245# wav --> ape
17246sub encode_wav_to_ape {
17247   my %args = ( @_ );
17248   my $row = $args{row}; my $file_ape = $args{file_ape}; my $file_wav = $args{file_wav};
17249
17250   my $Title   = $files_info[$row]{title};
17251   my $Artist  = $files_info[$row]{artist};
17252   my $Comment = $files_info[$row]{comment};
17253   my $Album   = $files_info[$row]{album};
17254   my $Genre   = $files_info[$row]{genre};
17255   my $Year    = $files_info[$row]{year};
17256   my $Track_Number = $files_info[$row]{track};
17257   my $Total_Track  = $files_info[$row]{total_track};
17258
17259	my $compress = $spinner_compress->get_value_as_int();
17260	$additional_command_ape = $entry_command_ape->get_text;
17261	my $cmd = "$nice_path -n $priority $ape_path $additional_command_ape ";
17262
17263	my  $file_in   = $directory_output . '/' . $file_wav;
17264	my  $file_out  = $directory_final  . '/' . $file_ape;
17265	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
17266	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
17267	print "encode_wav_to_ape --> Could not find wave file: $file_in\n" if( ! -e filename_from_unicode $file_in );
17268
17269
17270	insert_msg("." , "small-ini");
17271	insert_msg("$cmd \"$file_in2\" \"$file_out2\" -c$compress " , "small"); #print on debug textview
17272	insert_msg("\n" , "small");
17273
17274	# Use 'filename_from_unicode' to convert from unicode to local encoding.
17275	$file_in2  = filename_from_unicode $file_in2;
17276	$file_out2 = filename_from_unicode $file_out2;
17277	_utf8_on($file_in2);  # Don't let Perl to convert $file_in2 to unicode. See 'man Encode'.
17278	_utf8_on($file_out2); # Turns on the UTF8 flag in $file_out2 string.
17279	$cmd .= " \"$file_in2\" \"$file_out2\" -c$compress ";
17280
17281	# To exchange a command's STDOUT and STDERR in order to capture the STDERR
17282	my $pid = open( APEENC, "$cmd 3>&1 1>&2 2>&3  |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
17283	$pid_ape = $pid; ## set ape pid, pid > 0
17284
17285	my $div = 0; # initial value
17286	my $saved_value = 0;
17287
17288	$/="\r"; ## loop for output - initial
17289	while( <APEENC> )
17290	{
17291	        #output:
17292		#--- Monkey's Audio Console Front End (v3.96b8) (c) Matthew T. Ashland ---
17293                #Decompressing ...
17294                #Progress:  3.0% (357.7 sec remaining, 368.8 sec total)
17295
17296		if( $_ =~ /^Progress:\s+(\d+[\.,]\d)%\s\((.*)\)/ )
17297		{
17298		  #print "\n<<>> $_ --->1=$1  --->2=$2\n";
17299		  $saved_value = $div;
17300		  $div = sprintf("%0.2f", $1/100 );
17301		  $div = number_value($div);
17302		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
17303
17304                  #                            show only not repeated value
17305		  if ( $pbar_encode->get_fraction() <=1 and $div > $saved_value ) {
17306		  $pbar_encode->set_fraction( $div ) ;
17307		  $status_bar->push($context_id, " ape encoding $1% | $2");
17308		  #print ( " div  = $div \n");
17309		  }
17310		}
17311		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
17312		if ( not $normalize_button->get_active and $pid_ape >= 0 ) {
17313		    $pid_ape = StopProcessing($pid_ape," Stop ape encoding!", $file_wav);
17314		    $/="\n";  # abandon loop
17315		}
17316	}
17317	$/="\n"; ## final of loop
17318	close( APEENC );
17319
17320        if( -e filename_from_unicode $file_out ) {
17321
17322	   remove_apetag (%args, (filepath => $file_out) );
17323	   $metadata{'Encoded by'} = "MAC with gnormalize (http://gnormalize.sourceforge.net)";
17324	   write_apetag  (%args, (filepath => $file_out) );
17325        }
17326        else { print "encode_wav_to_ape --> Could not find ape file: $file_out\n"; }
17327
17328	if ($normalize_button->get_active) {  # if normalize button is active
17329	    $pid_ape = -1;                    # reset pid on the final of process
17330            return $true;    }
17331	else { return $false;}
17332}
17333
17334#  wav --> flac
17335sub encode_wav_to_flac {
17336   my %args = ( @_ );
17337   my $row = $args{row}; my $file_flac = $args{file_flac}; my $file_wav = $args{file_wav}; my $filepath = $args{filepath};
17338
17339   my $Title   = $files_info[$row]{title};
17340   my $Artist  = $files_info[$row]{artist};
17341   my $Comment = $files_info[$row]{comment};
17342   my $Album   = $files_info[$row]{album};
17343   my $Genre   = $files_info[$row]{genre};
17344   my $Year    = $files_info[$row]{year};
17345   my $Track_Number = $files_info[$row]{track};
17346   my $Total_Track  = $files_info[$row]{total_track};
17347
17348	my $cmd; # command
17349	my $bitrate;
17350	my $mode;
17351	my $freq;
17352	my $compress = $spinner_compress_flac->get_value();
17353
17354        ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max) = encode_settings(%args);
17355
17356	$additional_command_flac = $entry_command_flac->get_text;
17357	$cmd = "$nice_path -n $priority $flac_path -$compress -f $additional_command_flac ";
17358
17359	my  $file_in   = $directory_output . '/' . $file_wav;
17360	my  $file_out  = $directory_final  . '/' . $file_flac;
17361	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
17362	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
17363	print "encode_wav_to_flac --> Could not find wave file: $file_in\n" if( ! -e filename_from_unicode $file_in );
17364
17365	insert_msg("." , "small-ini");
17366	insert_msg("$cmd \"$file_in2\" -o \"$file_out2\"" , "small"); #print on debug textview
17367	insert_msg("\n" , "small");
17368
17369	# Use 'filename_from_unicode' to convert from unicode to local encoding.
17370	$file_in2  = filename_from_unicode $file_in2;
17371	$file_out2 = filename_from_unicode $file_out2;
17372	_utf8_on($file_in2);  # Don't let Perl to convert $file_in2 to unicode. See 'man Encode'.
17373	_utf8_on($file_out2); # Turns on the UTF8 flag in $file_out2 string.
17374	$cmd .= " \"$file_in2\" -o \"$file_out2\" ";
17375
17376	my $pid = open( FLAC, "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
17377	$pid_flac_decode = $pid; ## set flac pid, pid > 0
17378
17379	my $div = 0; # initial value
17380	my $saved_value = 0;
17381
17382	## loop for output - initial
17383	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
17384	while( <FLAC> )
17385	{
17386	        #output:options: -P 4096 -b 4608 -m -l 8 -q 0 -r 3,3
17387                #audio.wav: 51% complete, ratio=0.717
17388
17389		if( $_ =~ /.*:\s+(\d+)%\scomplete/ )
17390		{
17391		  #print "\n<<>> $_ --->1=$1 \n";
17392		  $saved_value = $div;
17393		  $div = sprintf("%0.2f", $1/100 );
17394		  $div = number_value($div);
17395		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
17396
17397                  #                             show only not repeated value
17398		  if ( $pbar_encode->get_fraction() <=1 and $div > $saved_value ) {
17399		     $pbar_encode->set_fraction( $div ) ;
17400		     $status_bar->push($context_id, " flac encoding $1%");
17401		  }
17402		}
17403		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
17404		if ( not $normalize_button->get_active and $pid_flac_decode >= 0 ) {
17405		    $pid_flac_decode = StopProcessing($pid_flac_decode," Stop flac encoding!", $file_wav);
17406		    $/="\n";  # abandon loop
17407		}
17408	}
17409	$/="\n"; ## final of loop
17410	close(FLAC);
17411
17412	# tag commands -- to see the entries: metaflac --list music.flac
17413	# --no-utf8-convert
17414	#$Album= encode("utf8",$Album);
17415
17416	my   @cmd_tag = ( $metaflac_path, '--no-utf8-convert', "--set-tag=Title=$Title", "--set-tag=Artist=$Artist" );
17417        push @cmd_tag,  ( "--set-tag=Album=$Album", "--set-tag=Date=$Year", "--set-tag=Description=$Comment", "--set-tag=Comment=$Comment" );
17418        if ($Total_Track ne "" and $Total_Track >= $Track_Number){
17419       	       push @cmd_tag, ( "--set-tag=Tracknumber=$Track_Number/$Total_Track" ); }
17420        else { push @cmd_tag, ( "--set-tag=Tracknumber=$Track_Number" );              }
17421        push @cmd_tag, ( "--set-tag=Genre=$Genre" );
17422
17423
17424	if ( $metaflac_path ne "" and $normalize_button->get_active ){
17425	   insert_msg("." , "small-ini");
17426	   insert_msg("@cmd_tag \"$file_out\"" , "small"); #print on debug textview
17427	   insert_msg("\n" , "small");
17428
17429	   push @cmd_tag, ( filename_from_unicode $file_out );
17430	   exec_cmd_system3(@cmd_tag);
17431	}
17432
17433	if ($normalize_button->get_active) {  # if normalize button is active
17434	    $pid_flac_decode = -1;            # reset pid on the final of process
17435            return $true;    }
17436	else { return $false;}
17437}
17438
17439#  wav --> mp4
17440sub encode_wav_to_mp4 {
17441   my %args = ( @_ );
17442   my $row = $args{row}; my $file_mp4 = $args{file_mp4}; my $file_wav = $args{file_wav}; my $filepath = $args{filepath};
17443
17444   my $Title   = $files_info[$row]{title};
17445   my $Artist  = $files_info[$row]{artist};
17446   my $Comment = $files_info[$row]{comment};
17447   my $Album   = $files_info[$row]{album};
17448   my $Genre   = $files_info[$row]{genre};
17449   my $Year    = $files_info[$row]{year};
17450   my $Track_Number = $files_info[$row]{track};
17451   my $Total_Track  = $files_info[$row]{total_track};
17452
17453	my $cmd; # command
17454	my $bitrate;
17455	my $mode;
17456	my $freq;
17457	my $compress = $spinner_compress_flac->get_value();
17458
17459	$Track_Number =~ s/^0*//;   #remove the leading zero
17460	$Total_Track =~ s/^0*//;
17461
17462	# tag commands for mp4 file
17463	my $cmd_tag = " -w";
17464	if ( $Artist ne ""  ){ $cmd_tag .= " --artist \"$Artist\"";   }
17465	if ( $Title ne ""   ){ $cmd_tag .= " --title \"$Title\"";     }
17466	if ( $Album ne ""   ){ $cmd_tag .= " --album \"$Album\"";     }
17467	if ( $Year ne ""    ){ $cmd_tag .= " --year \"$Year\"";       }
17468	if ( $Genre ne ""   ){ $cmd_tag .= " --genre \"$Genre\"";     }
17469	if ( $Comment ne "" ){ $cmd_tag .= " --comment \"$Comment\""; }
17470
17471	if ( $Total_Track ne "" and $Total_Track >= $Track_Number ){ $cmd_tag .= " --track \"$Track_Number/$Total_Track\" "; }
17472	else { $cmd_tag .= " --track \"$Track_Number\" "; }
17473
17474        ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max) = encode_settings(%args);
17475
17476	$cmd = "$nice_path -n $priority $faac_path -c $freq ";
17477	# use --no-tns and --no-midslide to play correctly in the Quicktime player
17478	if ( $encode eq "variable" and $check_change_properties->get_active ){
17479	       $cmd .= " -q $Vmp4 ";    } # VBR (variable bitrate)
17480	else{  $cmd .= " -b $bitrate "; } # ABR (average bitrate)
17481
17482	if ( $file_mp4 !~ /\.aac$/i ) { $cmd .= " $cmd_tag"; } #tag only for mp4|m4a
17483
17484	$additional_command_mp4 = $entry_command_mp4->get_text;
17485	$cmd .= " $additional_command_mp4 ";
17486
17487        my  $file_in   = $directory_output . '/' . $file_wav;
17488	my  $file_out  = $directory_final  . '/' . $file_mp4;
17489	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
17490	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
17491	print "encode_wav_to_mp4 --> Could not find wave file: $file_in\n" if( ! -e filename_from_unicode $file_in );
17492
17493	$cmd .= " \"$file_in2\" -o \"$file_out2\" ";
17494
17495	insert_msg("." , "small-ini");
17496	insert_msg("$cmd" , "small"); #print on debug textview
17497	insert_msg("\n" , "small");
17498
17499	my $pid = open( AAC, filename_from_unicode "$cmd 2>&1 |" ) or die "Couldn't execute \"$cmd\": $!, stopped\n";
17500	my $pid_faac_encode = $pid; ## set flac pid, pid > 0
17501
17502	my $div = 0; # initial value
17503	my $saved_value = 0;
17504
17505	## loop for output - initial
17506	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
17507	while( <AAC> )
17508	{
17509		# output:  frame          | bitrate | elapsed/estim | play/CPU | ETA
17510		#         300/4944  (  6%)|  240.2  |    1.2/19.4   |    5.90x | 18.3
17511                #        4944/4944  (100%)|  217.8  |   18.9/18.9   |    6.08x | 0.0
17512
17513		if( $_ =~ /.*\(\s*(\d+)%\)\|\s+(\d+).*\s+(\d+[\.,]\d+)x\s+\|\s+(\d+[\.,]\d+).*/ )
17514		{
17515		  $saved_value = $div;
17516		  $div = sprintf("%0.2f", $1/100 ); # eg (65%)/100
17517		  $div = number_value($div);
17518		  if ( $div > 1){ $div = 1;}  # to avoid possible errors
17519		  my $bitr = $2;
17520		  my $speed = sprintf("%.1f", $3 );
17521		  my $time = sprintf("%02d", $4 );
17522		  my $pct = sprintf("%02d", $1 ); # 02d : leading zero
17523
17524                  #                             show only not repeated value
17525		  if ( $pbar_encode->get_fraction() <=1 and $div > $saved_value ) {
17526		     $pbar_encode->set_fraction( $div ) ;
17527		     $status_bar->push($context_id, " encoding $pct% | bitrate = $bitr | time = $time sec | speed = $speed ");
17528		  }
17529		}
17530		while ( Gtk2->events_pending() and $normalize_button->get_active ) { Gtk2->main_iteration() }
17531		if ( not $normalize_button->get_active and $pid_faac_encode >= 0 ) {
17532		    $pid_faac_encode = StopProcessing($pid_faac_encode," Stop faac encoding!", $file_wav);
17533		    $/="\n";  # abandon loop
17534		}
17535	}
17536	$/="\n"; ## final of loop
17537	close(AAC);
17538
17539	if ($normalize_button->get_active) {  # if normalize button is active
17540	    $pid_faac_encode = -1;            # reset pid on the final of process
17541            return $true;    }
17542	else { return $false;}
17543}
17544
17545# wav --> wma : encode with ffmpeg
17546
17547   # see 'ffmpeg -h'; usage: ffmpeg [[infile options] -i infile]... {[outfile options] outfile}
17548   # -i filename         input file name
17549   # -y                  overwrite output files
17550   # -aq quality         set audio quality (codec-specific)
17551   # -ar rate            set audio sampling rate (in Hz)
17552   # -ac channels        set number of audio channels
17553   # -ab <int>     E..A. set bitrate (in bits/s)
17554
17555   # -title    string    set the title
17556   # -author   string    set the author
17557   # -album    string    set the album
17558   # -comment  string    set the comment
17559   # -track    <int>   E.... set the track number
17560   # -year     <int>   E.... set the year
17561
17562   # encode: ffmpeg -y -i input.wav  -ab 200k -ar 44100 -ac 2 -title "tito��" -author "aut��" -album "alb��" -comment "com��"  -track "3" -year "2008"  output.wma
17563   # decode: ffmpeg -y -i input.wma  output.wav
17564
17565sub encode_wav_to_wma { # Not used yet
17566   my %args = ( @_ );
17567   my $row = $args{row}; my $file_mp3 = $args{file_mp3}; my $file_wav = $args{file_wav};
17568
17569   $file_mp3 = 'output.wma';
17570
17571   my $filepath = $args{filepath};
17572
17573   # get all the 8 tags
17574   my $Title   = $files_info[$row]{title};
17575   my $Artist  = $files_info[$row]{artist};
17576   my $Album   = $files_info[$row]{album};
17577   my $Comment = $files_info[$row]{comment};
17578   my $Year    = $files_info[$row]{year};
17579   my $Genre   = $files_info[$row]{genre};
17580   my $Track_Number = $files_info[$row]{track};
17581   my $Total_Track  = $files_info[$row]{total_track};
17582
17583   my $bitrate;
17584   my $mode;
17585   my $freq;
17586
17587   my  $file_in   = $directory_output . '/' . $file_wav;
17588   my  $file_out  = $directory_final  . '/' . $file_mp3;
17589  (my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
17590  (my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
17591   print "encode_wav_to_wma --> Could not find wave file: $file_in\n" if( ! -e filename_from_unicode $file_in );
17592
17593   # tag commands for wma file
17594   my $cmd_tag = " ";
17595   if ( $Artist ne ""  ){ $cmd_tag .= " -author \"$Artist\"";   }
17596   if ( $Title ne ""   ){ $cmd_tag .= " -title \"$Title\"";     }
17597   if ( $Album ne ""   ){ $cmd_tag .= " -album \"$Album\"";     }
17598   if ( $Year ne ""    ){ $cmd_tag .= " -year \"$Year\"";       }
17599  #if ( $Genre ne ""   ){ $cmd_tag .= " -genre \"$Genre\"";     }
17600   if ( $Comment ne "" ){ $cmd_tag .= " -comment \"$Comment\""; }
17601
17602   #if ( $Total_Track ne "" and $Total_Track >= $Track_Number ){ $cmd_tag .= " -track \"$Track_Number/$Total_Track\" "; }
17603   #else { $cmd_tag .= " -track \"$Track_Number\" "; }
17604
17605   ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max) = encode_settings(%args);
17606   $additional_command_mp3 = $entry_command_mp3->get_text;
17607
17608   #my $cmd = "$nice_path -n $priority /usr/bin/ffmpeg -y -i \"$file_in2\" -ab ${bitrate}k -ar $freq -ac 2 $cmd_tag $additional_command_mp3 \"$file_out2\" ";
17609   my $cmd = " /usr/bin/ffmpeg -y -i \"$file_in2\" -ab ${bitrate}k -ar $freq -ac 2 \"$file_out2\"";
17610
17611   insert_msg("." , "small-ini");
17612   insert_msg("$cmd" , "small"); #print on debug textview
17613   insert_msg("\n" , "small");
17614
17615   ## start encoding process
17616   my $pid = open( FFMPEG, filename_from_unicode "$cmd 2>&1 |" ) or die "couldn't execute \"$cmd\": $!\n";
17617   $pid_lame_encode = $pid;  # set lame encode pid
17618
17619   my $div = 0;
17620
17621   $/="\r"; ## loop for output - initial
17622   while( <FFMPEG> )
17623   {
17624      # size=     754kB time=25.1 bitrate= 245.7kbits/s
17625      # size=    1207kB time=40.3 bitrate= 245.3kbits/s
17626      print "aquiiii: $_\n";
17627
17628   }
17629   $/="\n"; ## final of loop
17630   close( FFMPEG );
17631
17632   if ($normalize_button->get_active) {  # if normalize button is active
17633      $pid_lame_encode = -1;            # reset pid on the final of process
17634      return $true;     }
17635   else { return $false;}
17636}
17637
17638
17639# wav --> mp3 : encode with lame
17640sub encode_wav_to_mp3 {
17641   my %args = ( @_ );
17642   my $row = $args{row}; my $file_mp3 = $args{file_mp3}; my $file_wav = $args{file_wav};
17643
17644   my $filepath = $args{filepath};
17645   #$filepath = filename_from_unicode $filepath if( ! -e $filepath );
17646
17647   # get all the 8 tags
17648   my $Title   = $files_info[$row]{title};
17649   my $Artist  = $files_info[$row]{artist};
17650   my $Album   = $files_info[$row]{album};
17651   my $Comment = $files_info[$row]{comment};
17652   my $Year    = $files_info[$row]{year};
17653   my $Genre   = $files_info[$row]{genre};
17654   my $Track_Number = $files_info[$row]{track};
17655   my $Total_Track  = $files_info[$row]{total_track};
17656
17657   my $bitrate;
17658   my $mode;
17659   my $freq;
17660
17661   ($encode,$mode,$bitrate,$freq,$vb_Min,$vb_Max) = encode_settings(%args);
17662
17663   my $cmd = "$nice_path -n $priority $lame_path -q $quality -c ";
17664
17665   if    ( $encode eq "constant"                                          ){ $cmd .= "--cbr -b $bitrate ";                              } # CBR (constant bitrate)
17666   elsif ( $encode eq "average"                                           ){ $cmd .= "--abr $bitrate -b $vb_Min -B $vb_Max --nohist ";  } # ABR (average bitrate)
17667   elsif ( $encode eq "variable" and $check_change_properties->get_active ){ $cmd .= "--vbr-new -V $V -b $vb_Min -B $vb_Max --nohist "; } # VBR (variable bitrate)
17668
17669	# common commands of lame for all options of encode
17670	$cmd .= "-m \"$mode\" --resample $freq --replaygain-fast ";
17671
17672	$additional_command_mp3 = $entry_command_mp3->get_text;
17673	$cmd .= " --add-id3v2 --pad-id3v2 $additional_command_mp3 ";
17674
17675	my  $file_in   = $directory_output . '/' . $file_wav;
17676	my  $file_out  = $directory_final  . '/' . $file_mp3;
17677	(my $file_in2  = $file_in ) =~ s/"/\\"/g;  # change all " characters by \"
17678	(my $file_out2 = $file_out) =~ s/"/\\"/g;  # change all " characters by \"
17679	print "encode_wav_to_mp3 --> Could not find wave file: $file_in\n" if( ! -e filename_from_unicode $file_in );
17680
17681	$cmd .= " \"$file_in2\" \"$file_out2\" ";
17682
17683	insert_msg("." , "small-ini");
17684	insert_msg("$cmd" , "small"); #print on debug textview
17685	insert_msg("\n" , "small");
17686
17687	## start encoding process
17688	my $pid = open( LAME, filename_from_unicode "$cmd 2>&1 |" ) or die "couldn't execute \"$cmd\": $!\n";
17689	$pid_lame_encode = $pid;  # set lame encode pid
17690
17691	my $div = 0;
17692
17693	## loop for output - initial
17694	$/="\r"; ## from http://acidrip.thirtythreeandathird.net/
17695	while( <LAME> )
17696	{
17697		## this is output of lame.
17698		#LAME version 3.96.1 (http://lame.sourceforge.net/)
17699                #CPU features: MMX (ASM used), 3DNow! (ASM used), SSE
17700                #Using polyphase lowpass filter, transition band: 17249 Hz - 17782 Hz
17701                #Encoding almir_sater-instrumental-03_vinheta_do_capeta.wav to lixo.mp3
17702                #Encoding as 44.1 kHz 128 kbps j-stereo MPEG-1 Layer III (11x) qval=3
17703                #   Frame          |  CPU time/estim | REAL time/estim | play/CPU |    ETA
17704		#   320/2171   ( 7%)|    0:05/    0:09|    0:05/    0:10|   5.8102x|    0:04
17705                #  1250/2171   (58%)|    0:05/    0:09|    0:05/    0:10|   5.8102x|    0:04
17706
17707		# print "aquiiii: $_\n";
17708
17709		if( $_ =~ /^\s.*\s*\(\s*(\d+)%\)\|\s*.*\s+(\d+[\.,]\d+)x\|\s+(\d+\:\d+).*/ )
17710		{
17711		  $div = sprintf("%0.2f", $1/100 ); # eg (65%)/100
17712		  $div = number_value($div);
17713		  #print ( "div = $1 ; speed = $2 \n");
17714		  my $speed = sprintf("%.1f", $2 );
17715		  my $time = $3;
17716		  my $pct = sprintf("%02d", $1 ); # 02d : leading zero
17717
17718		  if ( $pbar_encode->get_fraction() <=1 and $pid_lame_encode>=0 ) {
17719		     $pbar_encode->set_fraction( $div ) ;
17720		     $status_bar->push($context_id, " lame encode $pct% | time remaining = $time | encode speed = $speed");
17721		  }
17722		}
17723		while ( Gtk2->events_pending() ) { Gtk2->main_iteration() }
17724
17725		if ( not $normalize_button->get_active and $pid_lame_encode >= 0 ) {
17726		    $pid_lame_encode = StopProcessing($pid_lame_encode, $langs{msg058}, $file_wav );
17727		    $/="\n";  # abandon loop
17728		}
17729	}
17730	$/="\n"; ## final of loop
17731	close( LAME );
17732
17733	if ($normalize_button->get_active) {
17734	   print "encode_wav_to_mp3 --> Could not find mp3 file: $file_out\n" if( ! -e filename_from_unicode $file_out );
17735
17736	   # save the Total_Track in file_mp3 --- lame can not save the tag: 'track/Total_Track'
17737	   $metadata{'Encoded by'} = "LAME with gnormalize (http://gnormalize.sourceforge.net)";
17738	   # to save ID3v2 tag
17739	   save_ID3v2_mp3_tag($file_out, $Title, $Artist, $Album, $Year, $Comment, $Track_Number, $Total_Track, $Genre);
17740
17741           save_ID3v1_mp3_tag($file_out, $Title, $Artist, $Album, $Year, $Comment, $Track_Number, $Genre );
17742	}
17743
17744	if ($normalize_button->get_active) {  # if normalize button is active
17745	    $pid_lame_encode = -1;            # reset pid on the final of process
17746            return $true;    }
17747	else { return $false;}
17748}
17749
17750####----------------------Config----------------------####
17751change_font_for_all_child($window_font_name);
17752#encode_choice_sensitive();
17753$notebook->set_current_page ($notebook_page_num);
17754$notebook2->set_current_page ($notebook_page_num2);
17755
17756####-----------------------ARGV-----------------------####
17757
17758sub arguments {
17759
17760 # Get the parameters from the command line.
17761 if (@ARGV == 1){      #number of arguments
17762    my $file = filename_to_unicode $ARGV[0];
17763
17764    if ( ! -e filename_from_unicode $file ){ # if the file not exist
17765       print "This file not exist: $file\n";
17766       return;
17767    }
17768    $file =~ s/(.*\/)//g;      #copy and remove (path)/
17769    insert_msg( $langs{msg078}.": $file\n\n" , "small");
17770    ($directory = $ARGV[0]) =~ s/(.*)\/.*/$1\//g;
17771
17772    if ( $directory !~ /\// ){      # for the case of none directory: 'gnormalize music.mp3'
17773       $directory = filename_to_unicode "$ENV{'PWD'}"; # $directory = `pwd`
17774    }
17775    elsif ( $directory !~ /^\// ){  # for the case of directory like that: 'gnormalize some/place/music.mp3'
17776       $directory = filename_to_unicode "$ENV{'PWD'}/".$directory;
17777    }
17778    #print "file = $file ;; directory = $directory \n";
17779    fill_with_all_informations($directory . '/' . $file);
17780 }
17781}
17782
17783arguments();
17784
17785####---------------------- Quit ----------------------####
17786
17787# Quit of gnormalize
17788sub Quit {
17789   stop_playing_music();
17790   save_config();
17791   Gtk2->main_quit;
17792   return $true;
17793}
17794
17795#print Claudio1::teste(); # only test
17796#gnormalize::cdplay::read_toc();
17797#gnormalize::cdplay::play_cdrom_track();
17798#gnormalize::cdplay::play_cdrom_msf2();
17799#gnormalize::cdplay::play_cdrom_msf(8);
17800#gnormalize::cdplay::pause_cdrom();
17801#gnormalize::cdplay::resume_cdrom();
17802#gnormalize::cdplay::eject_cdrom();
17803#gnormalize::cdplay::multi();
17804#gnormalize::cdplay::get_vol();
17805#gnormalize::cdplay::set_vol(11,11,11,11);
17806#gnormalize::cdplay::cdrom_read_audio();
17807#print gnormalize::cdplay::msf_to_lba(0,0,0);
17808
17809#my $info = gnormalize::cdplay::Info();
17810#print "current track = $info->{current_track}";
17811
17812#gnormalize::factorial::print2(5);
17813
17814# Gtk event loop
17815Gtk2->main; # necess�rio para executar o programa
17816exit(0);
17817
17818####--------------------- The End ---------------------####
17819
17820#-------------------------------------------------#
17821#------------------ New-Package ------------------#
17822# See perlmod, perlmodlib, perltoot
17823# See Learning Perl Objects, References and Modules, Chapter 2, 1o. Edition, 2003.
17824
17825#{ # start scope block
17826#   package Claudio1;  # now in package Claudio1. Only one test
17827#   use strict;
17828   # See <man Exporter>
17829
17830#   sub teste {       # Claudio1::teste
17831#      print "Claudio1::teste\n";
17832#   }
17833#   1;  # don't forget to return a true value from the file
17834#} # end scope block
17835
17836#-------------------------------------------------#
17837#------------------ New-Package ------------------#
17838
17839######################-----gnormalize::cdplay-----######################
17840
17841{ # start scope block
17842package gnormalize::cdplay;  # New package to play Audio CD.
17843use strict;
17844use Fcntl;
17845
17846# require Exporter;
17847
17848# See <man Exporter>
17849our $VERSION = '0.01';
17850
17851# See <man perlopentut> and stat() on perlfunc.
17852# See "Linux::CDROM::Cookbook" (Linux-CDROM-0.01.tar.gz),
17853# DOING EVEN MORE section by Tassilo von Parseval
17854
17855# see /usr/include/linux/cdrom.h
17856use constant CDROMPAUSE => 0x5301;          # /* Pause Audio Operation */
17857use constant CDROMRESUME => 0x5302;         # /* Resume paused Audio Operation */
17858use constant CDROMPLAYMSF => 0x5303;        # /* Play Audio MSF (struct cdrom_msf) */
17859use constant CDROMPLAYTRKIND => 0x5304;     # /* Play Audio Track/index (struct cdrom_ti) */
17860use constant CDROMREADTOCHDR => 0x5305;     # /* Read TOC header (struct cdrom_tochdr) */
17861use constant CDROMREADTOCENTRY => 0x5306;   # /* Read TOC entry (struct cdrom_tocentry) */
17862use constant CDROMSTOP => 0x5307;           # /* Stop the cdrom drive */
17863use constant CDROMSTART => 0x5308;          # /* Start the cdrom drive */
17864use constant CDROMEJECT => 0x5309;          # /* Ejects the cdrom media */
17865use constant CDROM_LEADOUT => 0xAA;         # /* The leadout track is always 0xAA, regardless of # of tracks on disc */
17866use constant CDROM_AUDIO_COMPLETED => 0x13; # /* audio play successfully completed */
17867use constant CDROMVOLREAD => 0x5313;        # /* Get the drive's volume setting (struct cdrom_volctrl) */
17868use constant CDROMVOLCTRL => 0x530a;        # /* Control output volume (struct cdrom_volctrl) */
17869use constant CDROM_DRIVE_STATUS => 0x5326;  # /* Get tray position, etc. */
17870use constant CDROM_DISC_STATUS => 0x5327;   # /* Get disc type, etc. */
17871use constant CDROMSUBCHNL => 0x530b;        # /* Read subchannel data (struct cdrom_subchnl) */
17872use constant CDROMREADAUDIO => 0x530e;      # /* (struct cdrom_read_audio) */
17873use constant CDROMREADRAW => 0x5314;        # /* read data in raw mode (2352 Bytes)
17874use constant CDROMMULTISESSION => 0x5310;   # /* Obtain the start-of-last-session address of multi session disks
17875                                            #   (struct cdrom_multisession) */
17876use constant CD_MSF_OFFSET => 150;          # /* MSF numbering offset of first frame */
17877#/* CD-ROM address types (cdrom_tocentry.cdte_format) */
17878use constant CDROM_LBA => 0x01;             # /* "logical block": first frame is #0 */
17879use constant CDROM_MSF => 0x02;             # /* "minute-second-frame": binary, not bcd here! */
17880
17881   # /* This struct is used by the CDROMPLAYMSF ioctl */
17882   # struct cdrom_msf
17883   # {
17884   #	__u8	cdmsf_min0;	/* start minute */
17885   #	__u8	cdmsf_sec0;	/* start second */
17886   #	__u8	cdmsf_frame0;	/* start frame */
17887   #	__u8	cdmsf_min1;	/* end minute */
17888   #	__u8	cdmsf_sec1;	/* end second */
17889   #	__u8	cdmsf_frame1;	/* end frame */
17890   # };
17891
17892   # /* Address in either MSF or logical format */
17893   # union cdrom_addr
17894   # {
17895   #	struct cdrom_msf0	msf;  # 'CCCx' pack whith 32 bits = 4 bytes
17896   #	int			lba;  # 'i'    pack
17897   # };
17898
17899   # /* Address in MSF format */
17900   # struct cdrom_msf0
17901   # {
17902   #	__u8	minute;
17903   #	__u8	second;
17904   #	__u8	frame;
17905   # };
17906
17907   # /* This struct is used by the CDROMVOLCTRL and CDROMVOLREAD ioctls */
17908   # struct cdrom_volctrl
17909   # {
17910   #	__u8	channel0;
17911   #	__u8	channel1;
17912   #	__u8	channel2;
17913   #	__u8	channel3;
17914   # };
17915
17916sub msf_to_lba {
17917    my ($min, $sec, $frame) = @_;
17918    my $lba = ($min * 60 + $sec) * 75 + $frame - CD_MSF_OFFSET;
17919    return $lba; # Is equal to $ref_array->[$i]{begin_frames}
17920}
17921
17922sub play_cdrom_msf { # Play Audio cdrom on linux ;; Need read_toc() first.
17923   my $track  =  1; # track to play
17924   my $delta  =  0; # to advance or go back $delta seconds
17925   my $position  =  0.00; # 0.00 to 1.00 ; play at relative position into the track
17926   my $cd_toc = $toc_audiocd; # $toc_audiocd ; to find the frames of the tracks
17927
17928   # See Perl Cookbook, 2nd Edition, Ch. 10.7
17929   my %args = (
17930                Track => 1,     # default value
17931                Advance => 0,
17932		Position => 0.00,
17933                @_,             # argument pair list goes here
17934	      );
17935   if ( defined($args{Track}) ){ if ( $args{Track} =~ /^\d+$/ ){ $track = $args{Track}; } }
17936   else { return; }
17937   if ( $args{Advance} =~ /^[+-]?\d+$/ ){ $delta = $args{Advance}; }
17938   #gnormalize::cdplay::play_cdrom_msf(Track => $selected_row + 1, Position => 0.43);
17939   if ( $args{Position} =~ /^[\.\d]*$/ ){ $position = $args{Position}; }
17940
17941   my $info = Info(); # Get Status
17942   if ( not $info ){ return; }
17943   my $status = $info->{status};
17944   if ( $status eq 'invalid' or $status eq 'error' ){ return $false; }
17945
17946   my $startframe = $cd_toc->[$track-1]->{frames_abs};
17947
17948   if ( $delta != 0 ){ # advance $delta seconds
17949      my $new_frame = $info->{frame_abs}  + $delta * 75;  # current absolute frame + delta (seconds)
17950      if ( $new_frame > $startframe ) { $startframe = $new_frame; }
17951   }
17952   # 150 = 2*75, the gap before a audio track
17953   my $endframe = $cd_toc->[$track]->{frames_abs} - 1;
17954
17955   if ( $startframe < 0 or $endframe < 0 or $startframe >= $endframe ){ return $false; }
17956   if ( $position > 0 and $position <= 1 ){ $startframe = int($startframe + ($endframe - $startframe)*$position);}
17957
17958   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
17959
17960   my $min0   = int( $startframe / (60 * 75) );
17961   my $sec0   = int( ($startframe % (60 * 75)) / 75 );
17962   my $frame0 = int( $startframe % 75 );
17963   my $min1   = int( $endframe / (60 * 75) );
17964   my $sec1   = int( ($endframe / 75) % 60 );
17965   my $frame1 = int( $endframe % 75 );
17966   #print "startframe = $startframe ; endframe = $endframe ;; status = $status ; frames = ",$endframe - $startframe,"\n";
17967
17968   my $cdrom_msf = pack "C6", $min0, $sec0, $frame0, $min1, $sec1, $frame1;
17969
17970   # See what operations can be performed on your machine at /usr/include/sys/ioctl.h
17971   # Play Audio CD in MSF struct
17972   ioctl(CD, CDROMPLAYMSF, $cdrom_msf ) or die "can not play cdrom [$!] [$audiodevice_path]";
17973
17974   close(CD);
17975}
17976
17977sub play_cdrom_msf2 { # only for test
17978   if ( not Info() ){ return; } # Get Status
17979
17980   my $startframe = 202250; # 150: first pre gap
17981   my $endframe   = 207630;
17982
17983   if ( $startframe < 0 or $endframe < 0 or $startframe >= $endframe ){ return $false; }
17984
17985   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
17986
17987   my $min0   = int( $startframe / (60 * 75) );
17988   my $sec0   = int( ($startframe % (60 * 75)) / 75 );
17989   my $frame0 = int( $startframe % 75 );
17990   my $min1   = int( $endframe / (60 * 75) );
17991   my $sec1   = int( ($endframe / 75) % 60 );
17992   my $frame1 = int( $endframe % 75 );
17993   #print "startframe = $startframe ; endframe = $endframe ;; status = $status ; frames = $frames \n";
17994
17995   my $cdrom_msf = pack "C6", $min0, $sec0, $frame0, $min1, $sec1, $frame1;
17996
17997   # See what operations can be performed on your machine at /usr/include/sys/ioctl.h
17998   # Play Audio CD in MSF struct
17999   ioctl(CD, CDROMPLAYMSF, $cdrom_msf ) or die "can not play cdrom [$!] [$audiodevice_path]";
18000
18001   close(CD);
18002}
18003
18004
18005sub pause_cdrom { # Pause Audio cdrom on linux
18006   if ( not Info() ){ return; } # Get Status
18007   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
18008   ioctl(CD, CDROMPAUSE, 1 ) or die "can not pause cdrom [$!] [$audiodevice_path]";
18009   close(CD);
18010}
18011
18012sub resume_cdrom { # Resume Audio cdrom on linux
18013   if ( not Info() ){ return; } # Get Status
18014   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
18015   ioctl(CD, CDROMRESUME, 1 ) or die "can not resume cdrom [$!] [$audiodevice_path]";
18016   close(CD);
18017}
18018
18019sub stop_cdrom { # Stop Audio cdrom on linux
18020   if ( not Info() ){ return; } # Get Status
18021   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
18022   ioctl(CD, CDROMSTOP, 1 ) or die "can not stop cdrom [$!] [$audiodevice_path]";
18023   close(CD);
18024}
18025
18026sub eject_cdrom { # Eject Audio cdrom on linux
18027   if ( not Info() ){ return; } # Get Status
18028   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
18029   ioctl(CD, CDROMEJECT, 1 ) or die "can not stop cdrom [$!] [$audiodevice_path]";
18030   close(CD);
18031}
18032
18033sub set_vol { # Set Volume
18034   if ( not Info() ){ return; } # Get Status
18035   # Set Volume
18036   my $channel0=shift || 0;          #front_left  ; 0 - 255
18037   my $channel1=shift || $channel0;  #front_right
18038   my $channel2=shift || $channel0;  #back_left
18039   my $channel3=shift || $channel0;  #back_right
18040
18041   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
18042
18043   my $volctrl = pack "CCCC", $channel0, $channel1, $channel2, $channel3;
18044   ioctl(CD, CDROMVOLCTRL, $volctrl) or die "can not set volume [$!] [$audiodevice_path]";
18045
18046   close(CD);
18047}
18048
18049sub get_vol { # Get Volume
18050   if ( not Info() ){ return; } # Get Status
18051
18052   my ($channel0, $channel1, $channel2, $channel3) = ( 0, 0, 0, 0);
18053
18054   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
18055
18056   # Get Volume
18057   my $volctrl = pack "CCCC", $channel0, $channel1, $channel2, $channel3;
18058   ioctl(CD, CDROMVOLREAD, $volctrl) or die "can not read volume [$!] [$audiodevice_path]";
18059   ($channel0, $channel1, $channel2, $channel3) = unpack "CCCC", $volctrl;
18060   print "channel0 = $channel0 ; channel1 = $channel1 ; channel2 = $channel2 ; channel3 = $channel3\n";
18061
18062   close(CD);
18063}
18064
18065sub multi { # Show if the cdrom have multisession
18066   #/* This struct is used with the CDROMMULTISESSION ioctl */
18067   #struct cdrom_multisession
18068   #{
18069   #	union cdrom_addr addr; /* frame address: start-of-last-session
18070   #	                           (not the new "frame 16"!).  Only valid
18071   #	                           if the "xa_flag" is true. */
18072   #	__u8 xa_flag;        /* 1: "is XA disk" */
18073   #	__u8 addr_format;    /* CDROM_LBA or CDROM_MSF */
18074   #};
18075   my ($min, $sec, $frame) = (0, 0, 0);
18076   my $flag = 0;
18077   my $cdrom_multi = pack "CCCxCC", $min, $sec, $frame, $flag, CDROM_MSF; # Set the format
18078
18079   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or die "can not open cdrom [$!] [$audiodevice_path]";
18080   ioctl(CD, CDROMMULTISESSION, $cdrom_multi ) or die "multisession cdrom [$!] [$audiodevice_path]";
18081   close(CD);
18082   ( $min, $sec, $frame, $flag ) = unpack "CCCxC", $cdrom_multi;
18083   # print "min = $min; sec = $sec ; frame = $frame ; flag = $flag \n";
18084   # $flag = 1 if multisession
18085   return $flag;
18086}
18087
18088sub Info {  # Inform the absolute and relative time and current track of Audio CD
18089            # Need read_toc() first
18090
18091   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK) or return $false;
18092
18093   #/* This struct is used by the CDROMSUBCHNL ioctl */
18094   # struct cdrom_subchnl
18095   # {
18096   #	__u8	cdsc_format;       /* Set CDROM_LBA or CDROM_MSF format */
18097   #	__u8	cdsc_audiostatus;
18098   #	__u8	cdsc_adr:	4; /* (4 + 4) bits = 1 byte */
18099   #	__u8	cdsc_ctrl:	4;
18100   #	__u8	cdsc_trk;      # track
18101   #	__u8	cdsc_ind;      # index
18102   #    3x                     # <padding byte>
18103   #	union cdrom_addr cdsc_absaddr; #absolute address
18104   #	union cdrom_addr cdsc_reladdr; #relative address
18105   # };
18106
18107   my $cdrom_subchnl = pack "C", CDROM_MSF; # Set the format
18108   ioctl(CD, CDROMSUBCHNL, $cdrom_subchnl) or return $false;
18109   my ( $cdsc_format, $status, $cdsc_adr_ctrl, $cdsc_trk, $cdsc_ind,
18110        $min_abs, $sec_abs, $frame_abs, $min, $sec, $frame ) = unpack "C5x3CCCxCCCx", $cdrom_subchnl;
18111
18112   # status, see /usr/include/linux/cdrom.h:
18113   # 21 (decimal) = 0x15 (hexagonal) /* no current audio status to return */
18114   if    ( $status == 0x00 ) { $status = 'invalid';   }
18115   elsif ( $status == 0x11 ) { $status = 'play';      }
18116   elsif ( $status == 0x12 ) { $status = 'paused';    }
18117   elsif ( $status == 0x13 ) { $status = 'completed'; }
18118   elsif ( $status == 0x14 ) { $status = 'error';     }
18119   elsif ( $status == 0x15 ) { $status = 'no_status'; }
18120   else  { $status = 'no_disc'; }
18121
18122   my %info=();
18123
18124   # $cdsc_trk shows next track at boundary region!
18125   # See 'sub get_current_track' how to get the correct current track.
18126   $info{current_track}=$cdsc_trk; #this is not always correct! (when not exist pre gap = 150)
18127   $info{status}=$status;
18128
18129   $info{min_abs}=$min_abs;
18130   $info{sec_abs}=$sec_abs;
18131   $frame_abs = int($frame_abs + $sec_abs*75 + $min_abs*60*75); # current absolute frame
18132   $info{frame_abs}=$frame_abs;
18133   #$info{time}=[ $min_abs , $sec_abs , $frame_abs ];
18134
18135   $info{min}=$min;    # It shows regressive time to next track
18136   $info{sec}=$sec;
18137   $frame = int($frame + $sec*75 + $min*60*75);                 # current relative frame
18138   $info{frame}=$frame;
18139
18140   #print "track = $cdsc_trk ; index = $cdsc_ind ; status = $status ; format = $cdsc_format \n";
18141   #print "(min_abs = $min_abs ; sec_abs = $sec_abs ; frame_abs = $frame_abs) ;; (min = $min ; sec = $sec ; frame = $frame)\n";
18142
18143   close(CD);
18144
18145   return \%info;
18146}
18147
18148# See CDDB_get and Linux::CDROM::Cookbook for some sugestions
18149# See /usr/include/linux/cdrom.h
18150sub read_toc {
18151   my $cdplayer = shift; # only show special track for $cdplayer = "gnormalize::cdplay"
18152   my $device = shift || $audiodevice_path;
18153
18154   my $tochdr="";
18155   my $debug = 0; # values:0,1,2 ;;   if value==1, show output like 'cdparanoia -Q'
18156
18157   my $open = sysopen (CD, $device, O_RDONLY | O_NONBLOCK);
18158   if ( not defined($open) ){ return $false; }
18159
18160   my $result = ioctl(CD, CDROMREADTOCHDR, $tochdr) ; # or die "can not read toc [$!] [$audiodevice_path]";
18161   if ( not defined($result) ){ return $false; } # result = 0 but true
18162
18163   my ($start,$end) = unpack "CC",$tochdr;
18164
18165   # /* The leadout track is always 0xAA, regardless of # of tracks on disc */
18166   # The index "CDROM_LEADOUT" 0xAA = 170 (decimal) is the final address of last track.
18167   my @tracks=();
18168   for (my $i=$start; $i<=$end;$i++) { push @tracks,$i; }
18169   push @tracks,0xAA;
18170
18171   # /* Note that only "cdte_track" and "cdte_format" need to be set. All other slots
18172   # /* are only used for the return values.
18173   # /* This struct is used by the CDROMREADTOCENTRY ioctl */
18174   # struct cdrom_tocentry
18175   # {
18176   #	__u8	cdte_track;
18177   #	__u8	cdte_adr	:4;    /* two 4-bit wide fields require 1 byte
18178   #	__u8	cdte_ctrl	:4;
18179   #	__u8	cdte_format;           /* Set CDROM_LBA or CDROM_MSF format
18180   #	union cdrom_addr cdte_addr;    /* It's either a "struct cdrom_msf0" or an integer.
18181   #	__u8	cdte_datamode;
18182   # };
18183
18184   # /* Address in either MSF or logical format */
18185   # union cdrom_addr
18186   # {
18187   #	struct cdrom_msf0	msf;
18188   #	int			lba;
18189   # };
18190
18191   # /* Address in MSF format */
18192   # struct cdrom_msf0
18193   # {
18194   #	__u8	minute;
18195   #	__u8	second;
18196   #	__u8	frame;
18197   # };
18198
18199   print "\n     Tracks and absolutes frames:\n\n" if $debug==2;
18200
18201   my @array_toc=();
18202
18203   foreach my $i (@tracks) { # track 0xAA = 170 get the total frames
18204
18205     #            pack "C       # cdte_track                  Byte 0
18206     #                  C       # cdte_adr + cdte_ctrl        Byte 1
18207     #                  C       # cdte_format                 Byte 2
18208     #                  x       # <padding byte>
18209     #                  i       # cdte_addr
18210     #                  C       # cdte_datamode               Byte 8
18211     #                          ", track, 0, CDROM_LBA;
18212
18213     # if format = CDROM_MSF, then substitute "i" by "CCCx", both with 32 bits.
18214
18215
18216     my $tocentry=pack "CCCxCCCxC", $i,0,CDROM_MSF;  # Set "cdte_track" and "cdte_format".
18217     ioctl(CD, CDROMREADTOCENTRY, $tocentry) or die "can not read track $i info [$!] [$audiodevice_path]";
18218     my ($track, $adr_ctrl, $format, $min, $sec, $frame, $mode) = unpack "CCCxCCCxC", $tocentry;
18219
18220     my %cdtoc=();
18221
18222     $cdtoc{track}=$track;
18223     $cdtoc{min_abs}=$min;
18224     $cdtoc{sec_abs}=$sec;
18225     $cdtoc{frame_abs}=$frame;
18226     my $frames_abs = int($frame+$sec*75+$min*60*75);
18227     $cdtoc{frames_abs}=$frames_abs; # Absolute position or StartSector
18228
18229     $cdtoc{data} = $false;
18230
18231     # /* bit to tell whether track is data or audio (cdrom_tocentry.cdte_ctrl) */
18232     # 0x04 (hex) = 4 (dec) = 100 (bin) is equivalent to 0100 0000 = 0x40 (using 8 bits: 4-bits + 4-bits)
18233     if($adr_ctrl & 0x40) { $cdtoc{data} = $true; }
18234     printf "track=%02d ; min=%02d ; sec=%02d ; frame=%02d ; frames_abs=$frames_abs", $track, $min, $sec, $frame if $debug==2;
18235     if($cdtoc{data} and $debug==2) { print "track $track is data\n"; }
18236     elsif($debug==2){ print "\n"; }
18237
18238     push @array_toc, \%cdtoc;
18239   }
18240   close(CD);
18241
18242   # Taking a reference to an array (See 'Learning Perl Objects, References and Modules')
18243   my $ref_array = \@array_toc;
18244
18245   my $no_cdaudio = $true; # search for at least one audio track
18246   for (my $i = 0; $i <= $end; $i++) {
18247      if ( not $ref_array->[$i]->{data}){
18248         $no_cdaudio = $false;
18249 	 $start = $i+1;
18250 	 last;
18251      }
18252   }
18253   if ($no_cdaudio){ return $false; }
18254
18255   # Very Special Case: When CD_MSF_OFFSET ( = $ref_array->[0]->{frames_abs} ) is not equal to 150 frames
18256   # define CD_MSF_OFFSET       150 /* MSF numbering offset of first frame */
18257   # For the case when the audio cd don't have the pre gap (with 150 frame) as first track.
18258   # k3b have this bad option for make audio cds.
18259   if ( $ref_array->[$start-1]->{frames_abs} > 75*20 and not $ref_array->[$start-1]->{data} and $cdplayer eq "gnormalize::cdplay" ){
18260      # put one element (one hash) to the front of the array. see perlfunc
18261      unshift (@array_toc, {frames_abs => 150, data => 0, track => 1, min_abs => 0, sec_abs => 2} );
18262      $end ++;
18263   }
18264   print "\nTable of contents (audio tracks only):\n". #Like 'cdparanoia -Q'
18265	  "track        length               begin\n".
18266	  "============================================\n" if $debug==1;
18267
18268   for (my $i = $start; $i <= $end; $i++) {  # find relative frames
18269
18270      # 11400 frames, for CDROMMULTISESSION
18271      # 11400: multisession offset , adjust the end of last audio track to be in the first session
18272      if ( $ref_array->[$i]->{data} and not $ref_array->[$i-1]->{data} and multi() ){
18273         if ( $ref_array->[$i-1]->{frames_abs} < $ref_array->[$i]->{frames_abs} - 11400 ){
18274            $ref_array->[$i]->{frames_abs} -= 11400;
18275            $end = $i; # abandon the loop
18276	 }
18277      }
18278      my $frames = $ref_array->[$i]->{frames_abs} - $ref_array->[$i-1]->{frames_abs}; # relative frames inter tracks
18279      my $begin_frames = $ref_array->[$i-1]->{frames_abs} - 150; # 150 is the offset (gap) of first frame.
18280
18281      my $min = $frames / (60 * 75);
18282      my $sec = ($frames / 75) % 60;
18283      $ref_array->[$i]{frames} = $frames;
18284      $ref_array->[$i]{min} = sprintf ( "%02d", $min);
18285      $ref_array->[$i]{sec} = sprintf ( "%02d", $sec);
18286      $ref_array->[$i]{begin_frames} = $begin_frames; # frames_abs - (150 frames = 2 sec of offset)
18287
18288      # like 'cdparanoia -Q'
18289      printf "%3d.  %7ld [%02d:%02d.%02d]  %7ld [%02d:%02d.%02d]\n", $i,
18290              $frames, ($frames / (60 * 75)), (($frames/75)%60), ($frames % 75),
18291              $begin_frames, ($begin_frames/(60*75)), (($begin_frames/75)%60), ($begin_frames % 75)
18292	      if $debug==1;
18293   }
18294   $ref_array->[0]{total_track} = $end - $start + 1; #set total audio track number, not always equal to $#array_toc
18295   my $total_frames = $ref_array->[$end]->{frames_abs} - $ref_array->[$start-1]->{frames_abs};
18296   $ref_array->[0]{total_frames} = $total_frames;
18297   printf "TOTAL %7ld [%02d:%02d.%02d]    (audio only)\n", $total_frames, ($total_frames/(60*75)),
18298                                       (($total_frames/75)%60), ($total_frames % 75) if $debug==1;
18299   print "total audio tracks = $end ; total audio frames = $total_frames \n" if $debug==2;
18300   print "frame0 end at = $ref_array->[0]->{frames_abs} ; frame1 end at = $ref_array->[1]->{frames_abs}\n" if $debug==2;
18301
18302   return \@array_toc;
18303}
18304
18305# Read Audio cdrom on linux and rip
18306sub cdrom_read_audio { # Don't work yet!
18307
18308   my $track   = shift || 8;   # track to rip
18309   my $nframes = shift || 1;   # 2646 /* The maximum possible returned bytes */
18310   #my $cd_toc = $toc_audiocd; # $toc_audiocd ; to find the frames of the tracks
18311   my $cd_toc = read_toc();
18312
18313   my $startframe = $cd_toc->[$track-1]->{frames_abs} - 150; #LBA
18314
18315   #/* This struct is used by the CDROMREADAUDIO ioctl */
18316   #struct cdrom_read_audio
18317   #{
18318   #	union cdrom_addr addr; /* frame address */
18319   #	__u8 addr_format;      /* CDROM_LBA or CDROM_MSF */
18320   #	int nframes;           /* number of frames to read at once */ ; 1 <= nframes <= 75
18321   #	__u8 __user *buf;      /* frame buffer (size: nframes*2352 bytes) */
18322   #};     # unary * Dereference-address operator. See perlop and perlref
18323           # Dereferencing means getting at the value that a reference points to.
18324	   # int *ptr; ptr = &count ; "&" stores the address of count in ptr
18325	   # total = *ptr;  The value in the address stored in ptr is assigned to total
18326	   # See 'Advanced Perl Programming', Chap. 1. ;; Programming Perl, 3a. Ed, Chap. 8.3
18327   # CD_FRAMESIZE_RAW = 2352
18328   # char *  s : While Perl passes arguments to functions by reference, C passes arguments by value; see perlxstut.
18329   # default Perl doesn't currently support the const char * type.
18330
18331   # "The stack is a special type of array that is used to pass information to functions and pass return
18332   # values from functions back to the calling statement." The Complete Reference 2nd Ed, pag 694.
18333
18334   # __u8 buff[CD_FRAMESIZE_RAW];
18335   #cda.buf = (__u8 *)&buff[0];
18336   #for(i = start_lba; i < end_lba; i++)
18337   #{
18338   #   memset(buff, 0, sizeof(buff));      # see <man memset>
18339   #   cda.addr.lba = i;
18340   #   printf("lba: %u\n", i);
18341   #   ioctl(fd, CDROMREADAUDIO, &cda);
18342   #   for(n = 0; n < CD_FRAMESIZE_RAW/2; n++){ buff[n] = __swab16(buff[n]);}
18343   #
18344   #   write(out, (__u8 *)buff, CD_FRAMESIZE_RAW);
18345   #}
18346   #close(fd); close(out);
18347
18348   #use Devel::Pointer;
18349   #$a = address_of($b);   # a = &b;
18350   #$b = deref($a);        # b = *a;
18351
18352   my $format;
18353   my $buffer_size = $nframes * 2352;
18354
18355   my @buf = ( ); for (my $i=0;$i<2352;$i++){$buf[$i]=1 }
18356   my $addr = \@buf;  # reference like '&' unary operator
18357   printf "startframe = $startframe; track = $track ; value = $$addr[0] ; buf = $addr, men = %p\n", $addr;
18358
18359   my $cdrom_rip = pack "iCiC", $startframe, CDROM_LBA, 1 , $$addr[0];  # SCALAR reference
18360
18361   sysopen (CD,$audiodevice_path, O_RDONLY | O_NONBLOCK)
18362                     or die "can not open cdrom [$!] [$audiodevice_path]";
18363
18364   ioctl(CD, CDROMREADAUDIO, $cdrom_rip )
18365                     or die "can not rip the audio data from cdrom [$!] [$audiodevice_path]";
18366
18367   ($startframe, $format, $nframes, $addr) = unpack "iCiC", $cdrom_rip;
18368
18369   # leave room for WAV header (44 bytes)
18370   #seek WAV, 44, 0;
18371   #print "cdrom_rip = $cdrom_rip; value = $$addr[0]; addr = $addr; startframe = $startframe\n";
18372
18373   open WAV, ">/tmp/track${track}.wav" or die $!;
18374   binmode WAV;
18375
18376   #print WAV $cdrom_rip;
18377   for (my $i=0;$i<2352;$i++){ print WAV $buf[$i]; }
18378   #syswrite(WAV, $buffer, 2352 );
18379   close(WAV);
18380
18381   close(CD);
18382}
18383
183841;
18385} # end scope block
18386
18387
18388#-------------------------------------------------#
18389#------------------ New-Package ------------------#
18390# See perlmod, perlmodlib, perltoot
18391# See Learning Perl Objects, References and Modules, Chapter 2, 1o. Edition, 2003.
18392#{
18393#   package gnormalize::factorial;    # use C functions
18394#   use strict;
18395                                    # See man Inline and Inline::C-Cookbook
18396   #use Inline C => Config => LIBS => '-lmac';      # libmac
18397   # Perl CODE goes here
18398
18399#   sub print2 {
18400#      printf "%3d! = %10d\n", $_, factorial($_) for 1..10;
18401#   }
18402#   1;
18403
18404   # C CODE goes here
18405#   use Inline C => Config => LIBS => '-lmac';
18406#   use Inline C =>
18407#   "
18408#      double factorial(double x) {
18409#         if (x < 2)  return 1;
18410#         return x * factorial(x - 1);
18411#      }
18412#   "
18413#}
18414
18415
18416########################-----MP3::Info-----########################
18417
18418
18419{ # start scope block
18420
18421package MP3::Info::Internal;
18422
18423require 5.006;
18424
18425use overload;
18426use strict;
18427use Carp;
18428
18429use vars qw(
18430	@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION $REVISION
18431	@mp3_genres %mp3_genres @winamp_genres %winamp_genres $try_harder
18432	@t_bitrate @t_sampling_freq @frequency_tbl %v1_tag_fields
18433	@v1_tag_names %v2_tag_names %v2_to_v1_names $AUTOLOAD
18434	@mp3_info_fields %rva2_channel_types
18435);
18436
18437@ISA = 'Exporter';
18438@EXPORT = qw(
18439	set_mp3tag get_mp3tag get_mp3info remove_mp3tag
18440	use_winamp_genres
18441);
18442@EXPORT_OK = qw(@mp3_genres %mp3_genres use_mp3_utf8);
18443%EXPORT_TAGS = (
18444	genres	=> [qw(@mp3_genres %mp3_genres)],
18445	utf8	=> [qw(use_mp3_utf8)],
18446	all	=> [@EXPORT, @EXPORT_OK]
18447);
18448
18449# $Id$
18450($REVISION) = ' $Revision: 1.19 $ ' =~ /\$Revision:\s+([^\s]+)/;
18451$VERSION = '1.20';
18452
18453=pod
18454
18455=head1 NAME
18456
18457MP3::Info - Manipulate / fetch info from MP3 audio files
18458
18459=head1 SYNOPSIS
18460
18461	#!perl -w
18462	use MP3::Info;
18463	my $file = 'Pearls_Before_Swine.mp3';
18464	set_mp3tag($file, 'Pearls Before Swine', q"77's",
18465		'Sticks and Stones', '1990',
18466		q"(c) 1990 77's LTD.", 'rock & roll');
18467
18468	my $tag = get_mp3tag($file) or die "No TAG info";
18469	$tag->{GENRE} = 'rock';
18470	set_mp3tag($file, $tag);
18471
18472	my $info = get_mp3info($file);
18473	printf "$file length is %d:%d\n", $info->{MM}, $info->{SS};
18474
18475=cut
18476
18477{
18478	my $c = -1;
18479	# set all lower-case and regular-cased versions of genres as keys
18480	# with index as value of each key
18481	%mp3_genres = map {($_, ++$c, lc, $c)} @mp3_genres;
18482
18483	# do it again for winamp genres
18484	$c = -1;
18485	%winamp_genres = map {($_, ++$c, lc, $c)} @winamp_genres;
18486}
18487
18488=pod
18489
18490	my $mp3 = new MP3::Info $file;
18491	$mp3->title('Perls Before Swine');
18492	printf "$file length is %s, title is %s\n",
18493		$mp3->time, $mp3->title;
18494
18495
18496=head1 DESCRIPTION
18497
18498=over 4
18499
18500=item $mp3 = MP3::Info-E<gt>new(FILE)
18501
18502OOP interface to the rest of the module.  The same keys
18503available via get_mp3info and get_mp3tag are available
18504via the returned object (using upper case or lower case;
18505but note that all-caps "VERSION" will return the module
18506version, not the MP3 version).
18507
18508Passing a value to one of the methods will set the value
18509for that tag in the MP3 file, if applicable.
18510
18511=cut
18512
18513sub new {
18514	my($pack, $file) = @_;
18515
18516	my $info = get_mp3info($file) or return undef;
18517	my $tags = get_mp3tag($file) || { map { ($_ => undef) } @v1_tag_names };
18518	my %self = (
18519		FILE		=> $file,
18520		TRY_HARDER	=> 0
18521	);
18522
18523	@self{@mp3_info_fields, @v1_tag_names, 'file'} = (
18524		@{$info}{@mp3_info_fields},
18525		@{$tags}{@v1_tag_names},
18526		$file
18527	);
18528
18529	return bless \%self, $pack;
18530}
18531
18532sub can {
18533	my $self = shift;
18534	return $self->SUPER::can(@_) unless ref $self;
18535	my $name = uc shift;
18536	return sub { $self->$name(@_) } if exists $self->{$name};
18537	return undef;
18538}
18539
18540sub AUTOLOAD {
18541	my($self) = @_;
18542	(my $name = uc $AUTOLOAD) =~ s/^.*://;
18543
18544	if (exists $self->{$name}) {
18545		my $sub = exists $v1_tag_fields{$name}
18546			? sub {
18547				if (defined $_[1]) {
18548					$_[0]->{$name} = $_[1];
18549					set_mp3tag($_[0]->{FILE}, $_[0]);
18550				}
18551				return $_[0]->{$name};
18552			}
18553			: sub {
18554				return $_[0]->{$name}
18555			};
18556
18557		no strict 'refs';
18558		*{$AUTOLOAD} = $sub;
18559		goto &$AUTOLOAD;
18560
18561	} else {
18562		carp(sprintf "No method '$name' available in package %s.",
18563			__PACKAGE__);
18564	}
18565}
18566
18567sub DESTROY {
18568
18569}
18570
18571
18572=item use_mp3_utf8([STATUS])
18573
18574Tells MP3::Info to (or not) return TAG info in UTF-8.
18575TRUE is 1, FALSE is 0.  Default is TRUE, if available.
18576
18577Will only be able to turn it on if Encode is available.  ID3v2
18578tags will be converted to UTF-8 according to the encoding specified
18579in each tag; ID3v1 tags will be assumed Latin-1 and converted
18580to UTF-8.
18581
18582Function returns status (TRUE/FALSE).  If no argument is supplied,
18583or an unaccepted argument is supplied, function merely returns status.
18584
18585This function is not exported by default, but may be exported
18586with the C<:utf8> or C<:all> export tag.
18587
18588=cut
18589
18590my $unicode_module = eval { require Encode; require Encode::Guess };
18591my $UNICODE = use_mp3_utf8($unicode_module ? 1 : 0);
18592
18593sub use_mp3_utf8 {
18594	my $val = shift;
18595	no warnings;
18596
18597	$UNICODE = 0;
18598
18599	if ($val == 1) {
18600
18601		if ($unicode_module) {
18602			$Encode::Guess::NoUTFAutoGuess = 1;
18603			$UNICODE = 1;
18604		}
18605	}
18606
18607	return $UNICODE;
18608}
18609
18610=pod
18611
18612=item use_winamp_genres()
18613
18614Puts WinAmp genres into C<@mp3_genres> and C<%mp3_genres>
18615(adds 68 additional genres to the default list of 80).
18616This is a separate function because these are non-standard
18617genres, but they are included because they are widely used.
18618
18619You can import the data structures with one of:
18620
18621	use MP3::Info qw(:genres);
18622	use MP3::Info qw(:DEFAULT :genres);
18623	use MP3::Info qw(:all);
18624
18625=cut
18626
18627sub use_winamp_genres {
18628	%mp3_genres = %winamp_genres;
18629	@mp3_genres = @winamp_genres;
18630	return 1;
18631}
18632
18633=pod
18634
18635=item remove_mp3tag (FILE [, VERSION, BUFFER])
18636
18637Can remove ID3v1 or ID3v2 tags.  VERSION should be C<1> for ID3v1
18638(the default), C<2> for ID3v2, and C<ALL> for both.
18639
18640For ID3v1, removes last 128 bytes from file if those last 128 bytes begin
18641with the text 'TAG'.  File will be 128 bytes shorter.
18642
18643For ID3v2, removes ID3v2 tag.  Because an ID3v2 tag is at the
18644beginning of the file, we rewrite the file after removing the tag data.
18645The buffer for rewriting the file is 4MB.  BUFFER (in bytes) ca
18646change the buffer size.
18647
18648Returns the number of bytes removed, or -1 if no tag removed,
18649or undef if there is an error.
18650
18651=cut
18652
18653sub remove_mp3tag {
18654	my($file, $version, $buf) = @_;
18655	my($fh, $return);
18656
18657	$buf ||= 4096*1024;  # the bigger the faster
18658	$version ||= 1;
18659
18660	if (not (defined $file && $file ne '')) {
18661		$@ = "No file specified";
18662		return undef;
18663	}
18664
18665	if (not -s $file) {
18666		$@ = "File is empty";
18667		return undef;
18668	}
18669
18670	if (ref $file) { # filehandle passed
18671		$fh = $file;
18672	} else {
18673		if (not open $fh, '+<', $file) {
18674			$@ = "Can't open $file: $!";
18675			return undef;
18676		}
18677	}
18678
18679	binmode $fh;
18680
18681	if ($version eq 1 || $version eq 'ALL') {
18682		seek $fh, -128, 2;
18683		my $tell = tell $fh;
18684		if (<$fh> =~ /^TAG/) {
18685			truncate $fh, $tell or carp "Can't truncate '$file': $!";
18686			$return += 128;
18687		}
18688	}
18689
18690	if ($version eq 2 || $version eq 'ALL') {
18691		my $v2h = _get_v2head($fh);
18692		if ($v2h) {
18693			local $\;
18694			seek $fh, 0, 2;
18695			my $eof = tell $fh;
18696			my $off = $v2h->{tag_size};
18697
18698			while ($off < $eof) {
18699				seek $fh, $off, 0;
18700				read $fh, my($bytes), $buf;
18701				seek $fh, $off - $v2h->{tag_size}, 0;
18702				print $fh $bytes;
18703				$off += $buf;
18704			}
18705
18706			truncate $fh, $eof - $v2h->{tag_size}
18707				or carp "Can't truncate '$file': $!";
18708			$return += $v2h->{tag_size};
18709		}
18710	}
18711
18712	_close($file, $fh);
18713
18714	return $return || -1;
18715}
18716
18717
18718=pod
18719
18720=item set_mp3tag (FILE, TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE [, TRACKNUM])
18721
18722=item set_mp3tag (FILE, $HASHREF)
18723
18724Adds/changes tag information in an MP3 audio file.  Will clobber
18725any existing information in file.
18726
18727Fields are TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE.  All fields have
18728a 30-byte limit, except for YEAR, which has a four-byte limit, and GENRE,
18729which is one byte in the file.  The GENRE passed in the function is a
18730case-insensitive text string representing a genre found in C<@mp3_genres>.
18731
18732Will accept either a list of values, or a hashref of the type
18733returned by C<get_mp3tag>.
18734
18735If TRACKNUM is present (for ID3v1.1), then the COMMENT field can only be
1873628 bytes.
18737
18738ID3v2 support may come eventually.  Note that if you set a tag on a file
18739with ID3v2, the set tag will be for ID3v1[.1] only, and if you call
18740C<get_mp3tag> on the file, it will show you the (unchanged) ID3v2 tags,
18741unless you specify ID3v1.
18742
18743=cut
18744
18745sub set_mp3tag {
18746	my($file, $title, $artist, $album, $year, $comment, $genre, $tracknum) = @_;
18747	my(%info, $oldfh, $ref, $fh);
18748	local %v1_tag_fields = %v1_tag_fields;
18749
18750	# set each to '' if undef
18751	for ($title, $artist, $album, $year, $comment, $tracknum, $genre,
18752		(@info{@v1_tag_names}))
18753		{$_ = defined() ? $_ : ''}
18754
18755	($ref) = (overload::StrVal($title) =~ /^(?:.*\=)?([^=]*)\((?:[^\(]*)\)$/)
18756		if ref $title;
18757	# populate data to hashref if hashref is not passed
18758	if (!$ref) {
18759		(@info{@v1_tag_names}) =
18760			($title, $artist, $album, $year, $comment, $tracknum, $genre);
18761
18762	# put data from hashref into hashref if hashref is passed
18763	} elsif ($ref eq 'HASH') {
18764		%info = %$title;
18765
18766	# return otherwise
18767	} else {
18768		carp(<<'EOT');
18769Usage: set_mp3tag (FILE, TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE [, TRACKNUM])
18770       set_mp3tag (FILE, $HASHREF)
18771EOT
18772		return undef;
18773	}
18774
18775	if (not (defined $file && $file ne '')) {
18776		$@ = "No file specified";
18777		return undef;
18778	}
18779
18780	if (not -s $file) {
18781		$@ = "File is empty";
18782		return undef;
18783	}
18784
18785	# comment field length 28 if ID3v1.1
18786	$v1_tag_fields{COMMENT} = 28 if $info{TRACKNUM};
18787
18788
18789	# only if -w is on
18790	if ($^W) {
18791		# warn if fields too long
18792		foreach my $field (keys %v1_tag_fields) {
18793			$info{$field} = '' unless defined $info{$field};
18794			if (length($info{$field}) > $v1_tag_fields{$field}) {
18795				carp "Data too long for field $field: truncated to " .
18796					 "$v1_tag_fields{$field}";
18797			}
18798		}
18799
18800		if ($info{GENRE}) {
18801			carp "Genre `$info{GENRE}' does not exist\n"
18802				unless exists $mp3_genres{$info{GENRE}};
18803		}
18804	}
18805
18806	if ($info{TRACKNUM}) {
18807		$info{TRACKNUM} =~ s/^(\d+)\/(\d+)$/$1/;
18808		unless ($info{TRACKNUM} =~ /^\d+$/ &&
18809			$info{TRACKNUM} > 0 && $info{TRACKNUM} < 256) {
18810			carp "Tracknum `$info{TRACKNUM}' must be an integer " .
18811				"from 1 and 255\n" if $^W;
18812			$info{TRACKNUM} = '';
18813		}
18814	}
18815
18816	if (ref $file) { # filehandle passed
18817		$fh = $file;
18818	} else {
18819		if (not open $fh, '+<', $file) {
18820			$@ = "Can't open $file: $!";
18821			return undef;
18822		}
18823	}
18824
18825	binmode $fh;
18826	$oldfh = select $fh;
18827	seek $fh, -128, 2;
18828	# go to end of file if no tag, beginning of file if tag
18829	seek $fh, (<$fh> =~ /^TAG/ ? -128 : 0), 2;
18830
18831	# get genre value
18832	$info{GENRE} = $info{GENRE} && exists $mp3_genres{$info{GENRE}} ?
18833		$mp3_genres{$info{GENRE}} : 255;  # some default genre
18834
18835	local $\;
18836	# print TAG to file
18837	if ($info{TRACKNUM}) {
18838		print pack 'a3a30a30a30a4a28xCC', 'TAG', @info{@v1_tag_names};
18839	} else {
18840		print pack 'a3a30a30a30a4a30C', 'TAG', @info{@v1_tag_names[0..4, 6]};
18841	}
18842
18843	select $oldfh;
18844
18845	_close($file, $fh);
18846
18847	return 1;
18848}
18849
18850=pod
18851
18852=item get_mp3tag (FILE [, VERSION, RAW_V2])
18853
18854Returns hash reference containing tag information in MP3 file.  The keys
18855returned are the same as those supplied for C<set_mp3tag>, except in the
18856case of RAW_V2 being set.
18857
18858If VERSION is C<1>, the information is taken from the ID3v1 tag (if present).
18859If VERSION is C<2>, the information is taken from the ID3v2 tag (if present).
18860If VERSION is not supplied, or is false, the ID3v1 tag is read if present, and
18861then, if present, the ID3v2 tag information will override any existing ID3v1
18862tag info.
18863
18864If RAW_V2 is C<1>, the raw ID3v2 tag data is returned, without any manipulation
18865of text encoding.  The key name is the same as the frame ID (ID to name mappings
18866are in the global %v2_tag_names).
18867
18868If RAW_V2 is C<2>, the ID3v2 tag data is returned, manipulating for Unicode if
18869necessary, etc.  It also takes multiple values for a given key (such as comments)
18870and puts them in an arrayref.
18871
18872If the ID3v2 version is older than ID3v2.2.0 or newer than ID3v2.4.0, it will
18873not be read.
18874
18875Strings returned will be in Latin-1, unless UTF-8 is specified (L<use_mp3_utf8>),
18876(unless RAW_V2 is C<1>).
18877
18878Also returns a TAGVERSION key, containing the ID3 version used for the returned
18879data (if TAGVERSION argument is C<0>, may contain two versions).
18880
18881=cut
18882
18883sub get_mp3tag {
18884	my ($file, $ver, $raw_v2, $find_ape) = @_;
18885	my ($tag, $v2h, $fh);
18886
18887	my $v1    = {};
18888	my $v2    = {};
18889	my $ape   = {};
18890	my %info  = ();
18891	my @array = ();
18892
18893	$raw_v2 ||= 0;
18894	$ver = !$ver ? 0 : ($ver == 2 || $ver == 1) ? $ver : 0;
18895
18896	if (not (defined $file && $file ne '')) {
18897		$@ = "No file specified";
18898		return undef;
18899	}
18900
18901	my $filesize = -s $file;
18902
18903	if (!$filesize) {
18904		$@ = "File is empty";
18905		return undef;
18906	}
18907
18908	if (ref $file) { # filehandle passed
18909		$fh = $file;
18910	} else {
18911		if (not open $fh, '<', $file) {
18912			$@ = "Can't open $file: $!";
18913			return undef;
18914		}
18915	}
18916
18917	binmode $fh;
18918
18919	# Try and find an APE Tag - this is where FooBar2k & others
18920	# store ReplayGain information
18921	if ($find_ape) {
18922
18923		$ape = _parse_ape_tag($fh, $filesize, \%info);
18924	}
18925
18926	if ($ver < 2) {
18927
18928		$v1 = _get_v1tag($fh, \%info);
18929
18930		if ($ver == 1 && !$v1) {
18931			_close($file, $fh);
18932			$@ = "No ID3v1 tag found";
18933			return undef;
18934		}
18935	}
18936
18937	if ($ver == 2 || $ver == 0) {
18938		($v2, $v2h) = _get_v2tag($fh);
18939	}
18940
18941	if (!$v1 && !$v2 && !$ape) {
18942		_close($file, $fh);
18943		$@ = "No ID3 tag found";
18944		return undef;
18945	}
18946
18947	if (($ver == 0 || $ver == 2) && $v2) {
18948
18949		if ($raw_v2 == 1 && $ver == 2) {
18950
18951			%info = %$v2;
18952
18953			$info{'TAGVERSION'} = $v2h->{'version'};
18954
18955		} else {
18956
18957			_parse_v2tag($raw_v2, $v2, \%info);
18958
18959			if ($ver == 0 && $info{'TAGVERSION'}) {
18960				$info{'TAGVERSION'} .= ' / ' . $v2h->{'version'};
18961			} else {
18962				$info{'TAGVERSION'} = $v2h->{'version'};
18963			}
18964		}
18965	}
18966
18967	unless ($raw_v2 && $ver == 2) {
18968		foreach my $key (keys %info) {
18969			if (defined $info{$key}) {
18970				$info{$key} =~ s/\000+.*//g;
18971				$info{$key} =~ s/\s+$//;
18972			}
18973		}
18974
18975		for (@v1_tag_names) {
18976			$info{$_} = '' unless defined $info{$_};
18977		}
18978	}
18979
18980	if (keys %info && exists $info{'GENRE'} && ! defined $info{'GENRE'}) {
18981		$info{'GENRE'} = '';
18982	}
18983
18984	_close($file, $fh);
18985
18986	return keys %info ? {%info} : undef;
18987}
18988
18989sub _get_v1tag {
18990	my ($fh, $info) = @_;
18991
18992	seek $fh, -128, 2;
18993	read($fh, my $tag, 128);
18994
18995	if (!defined($tag) || $tag !~ /^TAG/) {
18996
18997		return 0;
18998	}
18999
19000	if (substr($tag, -3, 2) =~ /\000[^\000]/) {
19001
19002		(undef, @{$info}{@v1_tag_names}) =
19003			(unpack('a3a30a30a30a4a28', $tag),
19004			ord(substr($tag, -2, 1)),
19005			$mp3_genres[ord(substr $tag, -1)]);
19006
19007		$info->{'TAGVERSION'} = 'ID3v1.1';
19008
19009	} else {
19010
19011		(undef, @{$info}{@v1_tag_names[0..4, 6]}) =
19012			(unpack('a3a30a30a30a4a30', $tag),
19013			$mp3_genres[ord(substr $tag, -1)]);
19014
19015		$info->{'TAGVERSION'} = 'ID3v1';
19016	}
19017
19018	if ($UNICODE) {
19019
19020		# Save off the old suspects list, since we add
19021		# iso-8859-1 below, but don't want that there
19022		# for possible ID3 v2.x parsing below.
19023		my $oldSuspects = $Encode::Encoding{'Guess'}->{'Suspects'};
19024
19025		for my $key (keys %{$info}) {
19026
19027			next unless $info->{$key};
19028
19029			# Try and guess the encoding.
19030			my $value = $info->{$key};
19031			my $icode = Encode::Guess->guess($value);
19032
19033			unless (ref($icode)) {
19034
19035				# Often Latin1 bytes are
19036				# stuffed into a 1.1 tag.
19037				Encode::Guess->add_suspects('iso-8859-1');
19038
19039				while (length($value)) {
19040
19041					$icode = Encode::Guess->guess($value);
19042
19043					last if ref($icode);
19044
19045					# Remove garbage and retry
19046					# (string is truncated in the
19047					# middle of a multibyte char?)
19048					$value =~ s/(.)$//;
19049				}
19050			}
19051
19052			$info->{$key} = Encode::decode(ref($icode) ? $icode->name : 'iso-8859-1', $info->{$key});
19053		}
19054
19055		Encode::Guess->set_suspects(keys %{$oldSuspects});
19056	}
19057
19058	return 1;
19059}
19060
19061sub _parse_v2tag {
19062	my ($raw_v2, $v2, $info) = @_;
19063
19064	# Make sure any existing TXXX flags are an array.
19065	# As we might need to append comments to it below.
19066	if ($v2->{'TXXX'} && ref($v2->{'TXXX'}) ne 'ARRAY') {
19067
19068		$v2->{'TXXX'} = [ $v2->{'TXXX'} ];
19069	}
19070
19071	# J.River Media Center sticks RG tags in comments.
19072	# Ugh. Make them look like TXXX tags, which is really what they are.
19073	if (ref($v2->{'COMM'}) eq 'ARRAY' && grep { /Media Jukebox/ } @{$v2->{'COMM'}}) {
19074
19075		for my $comment (@{$v2->{'COMM'}}) {
19076
19077			if ($comment =~ /Media Jukebox/) {
19078
19079				# we only want one null to lead.
19080				$comment =~ s/^\000+//g;
19081
19082				push @{$v2->{'TXXX'}}, "\000$comment";
19083			}
19084		}
19085	}
19086
19087	my $hash = $raw_v2 == 2 ? { map { ($_, $_) } keys %v2_tag_names } : \%v2_to_v1_names;
19088
19089	for my $id (keys %$hash) {
19090
19091		next if !exists $v2->{$id};
19092
19093		if ($id =~ /^UFID?$/) {
19094
19095			my @ufid_list = split(/\0/, $v2->{$id});
19096
19097			$info->{$hash->{$id}} = $ufid_list[1] if ($#ufid_list > 0);
19098
19099		} elsif ($id =~ /^RVA[D2]?$/) {
19100
19101			# Expand these binary fields. See the ID3 spec for Relative Volume Adjustment.
19102			if ($id eq 'RVA2') {
19103
19104				# ID is a text string
19105				($info->{$hash->{$id}}->{'ID'}, my $rvad) = split /\0/, $v2->{$id};
19106
19107				my $channel = $rva2_channel_types{ ord(substr($rvad, 0, 1, '')) };
19108
19109				$info->{$hash->{$id}}->{$channel}->{'REPLAYGAIN_TRACK_GAIN'} =
19110					sprintf('%f', _grab_int_16(\$rvad) / 512);
19111
19112				my $peakBytes = ord(substr($rvad, 0, 1, ''));
19113
19114				if (int($peakBytes / 8)) {
19115
19116					$info->{$hash->{$id}}->{$channel}->{'REPLAYGAIN_TRACK_PEAK'} =
19117						sprintf('%f', _grab_int_16(\$rvad) / 512);
19118				}
19119
19120			} elsif ($id eq 'RVAD' || $id eq 'RVA') {
19121
19122				my $rvad  = $v2->{$id};
19123				my $flags = ord(substr($rvad, 0, 1, ''));
19124				my $desc  = ord(substr($rvad, 0, 1, ''));
19125
19126				# iTunes appears to be the only program that actually writes
19127				# out a RVA/RVAD tag. Everyone else punts.
19128				for my $type (qw(REPLAYGAIN_TRACK_GAIN REPLAYGAIN_TRACK_PEAK)) {
19129
19130					for my $channel (qw(RIGHT LEFT)) {
19131
19132						my $val = _grab_uint_16(\$rvad) / 256;
19133
19134						# iTunes uses a range of -255 to 255
19135						# to be -100% (silent) to 100% (+6dB)
19136						if ($val == -255) {
19137							$val = -96.0;
19138						} else {
19139							$val = 20.0 * log(($val+255)/255)/log(10);
19140						}
19141
19142						$info->{$hash->{$id}}->{$channel}->{$type} = $flags & 0x01 ? $val : -$val;
19143					}
19144				}
19145			}
19146
19147		} elsif ($id =~ /^A?PIC$/) {
19148
19149			my $pic = $v2->{$id};
19150
19151			# if there is more than one picture, just grab the first one.
19152			if (ref($pic) eq 'ARRAY') {
19153				$pic = (@$pic)[0];
19154			}
19155
19156			use bytes;
19157
19158			my $valid_pic  = 0;
19159			my $pic_len    = 0;
19160			my $pic_format = '';
19161
19162			# look for ID3 v2.2 picture
19163			if ($pic && $id eq 'PIC') {
19164
19165				# look for ID3 v2.2 picture
19166				my ($encoding, $format, $picture_type, $description) = unpack 'Ca3CZ*', $pic;
19167				$pic_len = length($description) + 1 + 5;
19168
19169				# skip extra terminating null if unicode
19170				if ($encoding) { $pic_len++; }
19171
19172				if ($pic_len < length($pic)) {
19173					$valid_pic  = 1;
19174					$pic_format = $format;
19175				}
19176
19177			} elsif ($pic && $id eq 'APIC') {
19178
19179				# look for ID3 v2.3 picture
19180				my ($encoding, $format) = unpack 'C Z*', $pic;
19181
19182				$pic_len = length($format) + 2;
19183
19184				if ($pic_len < length($pic)) {
19185
19186					my ($picture_type, $description) = unpack "x$pic_len C Z*", $pic;
19187
19188					$pic_len += 1 + length($description) + 1;
19189
19190					# skip extra terminating null if unicode
19191					if ($encoding) { $pic_len++; }
19192
19193					$valid_pic  = 1;
19194					$pic_format = $format;
19195				}
19196			}
19197
19198			# Proceed if we have a valid picture.
19199			if ($valid_pic && $pic_format) {
19200
19201				my ($data) = unpack("x$pic_len A*", $pic);
19202
19203				if (length($data) && $pic_format) {
19204
19205					$info->{$hash->{$id}} = {
19206						'DATA'   => $data,
19207						'FORMAT' => $pic_format,
19208					}
19209				}
19210			}
19211
19212		} else {
19213			my $data1 = $v2->{$id};
19214
19215			# this is tricky ... if this is an arrayref,
19216			# we want to only return one, so we pick the
19217			# first one.  but if it is a comment, we pick
19218			# the first one where the first charcter after
19219			# the language is NULL and not an additional
19220			# sub-comment, because that is most likely to be
19221			# the user-supplied comment
19222			if (ref $data1 && !$raw_v2) {
19223				if ($id =~ /^COMM?$/) {
19224					my($newdata) = grep /^(....\000)/, @{$data1};
19225					$data1 = $newdata || $data1->[0];
19226				} elsif ($id !~ /^(?:TXXX?|PRIV)$/) {
19227					# We can get multiple User Defined Text frames in a mp3 file
19228					$data1 = $data1->[0];
19229				}
19230			}
19231
19232			$data1 = [ $data1 ] if ! ref $data1;
19233
19234			for my $data (@$data1) {
19235				# TODO : this should only be done for certain frames;
19236				# using RAW still gives you access, but we should be smarter
19237				# about how individual frame types are handled.  it's not
19238				# like the list is infinitely long.
19239				$data =~ s/^(.)//; # strip first char (text encoding)
19240				my $encoding = $1;
19241				my $desc;
19242
19243				# Comments & Unsyncronized Lyrics have the same format.
19244				if ($id =~ /^(COM[M ]?|USLT)$/) { # space for iTunes brokenness
19245
19246					$data =~ s/^(?:...)//;		# strip language
19247				}
19248
19249				if ($UNICODE) {
19250
19251					if ($encoding eq "\001" || $encoding eq "\002") {  # UTF-16, UTF-16BE
19252						# text fields can be null-separated lists;
19253						# UTF-16 therefore needs special care
19254						#
19255						# foobar2000 encodes tags in UTF-16LE
19256						# (which is apparently illegal)
19257						# Encode dies on a bad BOM, so it is
19258						# probably wise to wrap it in an eval
19259						# anyway
19260						$data = eval { Encode::decode('utf16', $data) } || Encode::decode('utf16le', $data);
19261
19262					} elsif ($encoding eq "\003") { # UTF-8
19263
19264						# make sure string is UTF8, and set flag appropriately
19265						$data = Encode::decode('utf8', $data);
19266
19267					} elsif ($encoding eq "\000") {
19268
19269						# Only guess if it's not ascii.
19270						if ($data && $data !~ /^[\x00-\x7F]+$/) {
19271
19272							# Try and guess the encoding, otherwise just use latin1
19273							my $dec = Encode::Guess->guess($data);
19274
19275							if (ref $dec) {
19276								$data = $dec->decode($data);
19277							} else {
19278								# Best try
19279								$data = Encode::decode('iso-8859-1', $data);
19280							}
19281						}
19282					}
19283
19284				} else {
19285
19286					# If the string starts with an
19287					# UTF-16 little endian BOM, use a hack to
19288					# convert to ASCII per best-effort
19289					my $pat;
19290					if ($data =~ s/^\xFF\xFE//) {
19291						$pat = 'v';
19292					} elsif ($data =~ s/^\xFE\xFF//) {
19293						$pat = 'n';
19294					}
19295
19296					if ($pat) {
19297						$data = pack 'C*', map {
19298							(chr =~ /[[:ascii:]]/ && chr =~ /[[:print:]]/)
19299								? $_
19300								: ord('?')
19301						} unpack "$pat*", $data;
19302					}
19303				}
19304
19305				# We do this after decoding so we could be certain we're dealing
19306				# with 8-bit text.
19307				if ($id =~ /^(COM[M ]?|USLT)$/) { # space for iTunes brokenness
19308
19309					$data =~ s/^(.*?)\000//;	# strip up to first NULL(s),
19310									# for sub-comments (TODO:
19311									# handle all comment data)
19312					$desc = $1;
19313
19314				} elsif ($id =~ /^TCON?$/) {
19315
19316					my ($index, $name);
19317
19318					# Turn multiple nulls into a single.
19319					$data =~ s/\000+/\000/g;
19320
19321					# Handle the ID3v2.x spec -
19322					#
19323					# just an index number, possibly
19324					# paren enclosed - referer to the v1 genres.
19325					if ($data =~ /^ \(? (\d+) \)?\000?$/sx) {
19326
19327						$index = $1;
19328
19329					# Paren enclosed index with refinement.
19330					# (4)Eurodisco
19331					} elsif ($data =~ /^ \( (\d+) \)\000? ([^\(].+)$/x) {
19332
19333						($index, $name) = ($1, $2);
19334
19335					# List of indexes: (37)(38)
19336					} elsif ($data =~ /^ \( (\d+) \)\000?/x) {
19337
19338						my @genres = ();
19339
19340						while ($data =~ s/^ \( (\d+) \)\000?//x) {
19341
19342							push @genres, $mp3_genres[$1];
19343						}
19344
19345						$data = \@genres;
19346					}
19347
19348					# Text based genres will fall through.
19349					if ($name && $name ne "\000") {
19350						$data = $name;
19351					} elsif (defined $index) {
19352						$data = $mp3_genres[$index];
19353					}
19354				}
19355
19356				if ($raw_v2 == 2 && $desc) {
19357					$data = { $desc => $data };
19358				}
19359
19360				if ($raw_v2 == 2 && exists $info->{$hash->{$id}}) {
19361
19362					if (ref $info->{$hash->{$id}} eq 'ARRAY') {
19363						push @{$info->{$hash->{$id}}}, $data;
19364					} else {
19365						$info->{$hash->{$id}} = [ $info->{$hash->{$id}}, $data ];
19366					}
19367
19368				} else {
19369
19370					# User defined frame
19371					if ($id eq 'TXXX') {
19372
19373						my ($key, $val) = split(/\0/, $data);
19374						$info->{uc($key)} = $val;
19375
19376					} elsif ($id eq 'PRIV') {
19377
19378						my ($key, $val) = split(/\0/, $data);
19379						$info->{uc($key)} = unpack('v', $val);
19380
19381					} else {
19382
19383						$info->{$hash->{$id}} = $data;
19384					}
19385				}
19386			}
19387		}
19388	}
19389}
19390
19391sub _get_v2tag {
19392	my($fh) = @_;
19393	my($off, $end, $myseek, $v2, $v2h, $hlen, $num, $wholetag);
19394
19395	$v2 = {};
19396	$v2h = _get_v2head($fh) or return;
19397
19398	if ($v2h->{major_version} < 2) {
19399		carp "This is $v2h->{version}; " .
19400		     "ID3v2 versions older than ID3v2.2.0 not supported\n"
19401		     if $^W;
19402		return;
19403	}
19404
19405	# use syncsafe bytes if using version 2.4
19406	# my $bytesize = ($v2h->{major_version} > 3) ? 128 : 256;
19407
19408	# alas, that's what the spec says, but iTunes and others don't syncsafe
19409	# the length, which breaks MP3 files with v2.4 tags longer than 128 bytes,
19410	# like every image file.
19411	my $bytesize = 256;
19412
19413	if ($v2h->{major_version} == 2) {
19414		$hlen = 6;
19415		$num = 3;
19416	} else {
19417		$hlen = 10;
19418		$num = 4;
19419	}
19420
19421	$off = $v2h->{ext_header_size} + 10;
19422	$end = $v2h->{tag_size} + 10; # should we read in the footer too?
19423
19424	seek $fh, $v2h->{offset}, 0;
19425	read $fh, $wholetag, $end;
19426
19427	$wholetag =~ s/\xFF\x00/\xFF/gs if $v2h->{unsync};
19428
19429	$myseek = sub {
19430		my $bytes = substr($wholetag, $off, $hlen);
19431		return unless $bytes =~ /^([A-Z0-9]{$num})/
19432			|| ($num == 4 && $bytes =~ /^(COM )/);  # stupid iTunes
19433		my($id, $size) = ($1, $hlen);
19434		my @bytes = reverse unpack "C$num", substr($bytes, $num, $num);
19435
19436		for my $i (0 .. ($num - 1)) {
19437			$size += $bytes[$i] * $bytesize ** $i;
19438		}
19439
19440		my $flags = {};
19441		if ($v2h->{major_version} > 3) {
19442			my @bits = split //, unpack 'B16', substr($bytes, 8, 2);
19443			$flags->{frame_unsync}       = $bits[14];
19444			$flags->{data_len_indicator} = $bits[15];
19445		}
19446
19447		return($id, $size, $flags);
19448	};
19449
19450	while ($off < $end) {
19451		my($id, $size, $flags) = &$myseek or last;
19452
19453		my $bytes = substr($wholetag, $off+$hlen, $size-$hlen);
19454
19455		my $data_len;
19456		if ($flags->{data_len_indicator}) {
19457			$data_len = 0;
19458			my @data_len_bytes = reverse unpack 'C4', substr($bytes, 0, 4);
19459			$bytes = substr($bytes, 4);
19460		        for my $i (0..3) {
19461				$data_len += $data_len_bytes[$i] * 128 ** $i;
19462		        }
19463		}
19464
19465		# perform frame-level unsync if needed (skip if already done for whole tag)
19466		$bytes =~ s/\xFF\x00/\xFF/gs if $flags->{frame_unsync} && !$v2h->{unsync};
19467
19468		# if we know the data length, sanity check it now.
19469		if ($flags->{data_len_indicator} && defined $data_len) {
19470		        carp "Size mismatch on $id\n" unless $data_len == length($bytes);
19471		}
19472
19473		if (exists $v2->{$id}) {
19474			if (ref $v2->{$id} eq 'ARRAY') {
19475				push @{$v2->{$id}}, $bytes;
19476			} else {
19477				$v2->{$id} = [$v2->{$id}, $bytes];
19478			}
19479		} else {
19480			$v2->{$id} = $bytes;
19481		}
19482		$off += $size;
19483	}
19484
19485	return($v2, $v2h);
19486}
19487
19488
19489=pod
19490
19491=item get_mp3info (FILE)
19492
19493Returns hash reference containing file information for MP3 file.
19494This data cannot be changed.  Returned data:
19495
19496	VERSION		MPEG audio version (1, 2, 2.5)
19497	LAYER		MPEG layer description (1, 2, 3)
19498	STEREO		boolean for audio is in stereo
19499
19500	VBR		boolean for variable bitrate
19501	BITRATE		bitrate in kbps (average for VBR files)
19502	FREQUENCY	frequency in kHz
19503	SIZE		bytes in audio stream
19504	OFFSET		bytes offset that stream begins
19505
19506	SECS		total seconds
19507	MM		minutes
19508	SS		leftover seconds
19509	MS		leftover milliseconds
19510	TIME		time in MM:SS
19511
19512	COPYRIGHT	boolean for audio is copyrighted
19513	PADDING		boolean for MP3 frames are padded
19514	MODE		channel mode (0 = stereo, 1 = joint stereo,
19515			2 = dual channel, 3 = single channel)
19516	FRAMES		approximate number of frames
19517	FRAME_LENGTH	approximate length of a frame
19518	VBR_SCALE	VBR scale from VBR header
19519
19520On error, returns nothing and sets C<$@>.
19521
19522=cut
19523
19524sub get_mp3info {
19525	my($file) = @_;
19526	my($off, $byte, $eof, $h, $tot, $fh);
19527
19528	if (not (defined $file && $file ne '')) {
19529		$@ = "No file specified";
19530		return undef;
19531	}
19532
19533	if (not -s $file) {
19534		$@ = "File is empty";
19535		return undef;
19536	}
19537
19538	if (ref $file) { # filehandle passed
19539		$fh = $file;
19540	} else {
19541		if (not open $fh, '<', $file) {
19542			$@ = "Can't open $file: $!";
19543			return undef;
19544		}
19545	}
19546
19547	$off = 0;
19548	$tot = 8192;
19549
19550	# Let the caller change how far we seek in looking for a header.
19551	if ($try_harder) {
19552		$tot *= $try_harder;
19553	}
19554
19555	binmode $fh;
19556	seek $fh, $off, 0;
19557	read $fh, $byte, 4;
19558
19559	if ($off == 0) {
19560		if (my $v2h = _get_v2head($fh)) {
19561			$tot += $off += $v2h->{tag_size};
19562			seek $fh, $off, 0;
19563			read $fh, $byte, 4;
19564		}
19565	}
19566
19567	$h = _get_head($byte);
19568	my $is_mp3 = _is_mp3($h);
19569
19570	# the head wasn't where we were expecting it.. dig deeper.
19571	unless ($is_mp3) {
19572
19573		# do only one read - it's _much_ faster
19574		$off++;
19575		seek $fh, $off, 0;
19576		read $fh, $byte, $tot;
19577
19578		my $i;
19579
19580		# now walk the bytes looking for the head
19581		for ($i = 0; $i < $tot; $i++) {
19582
19583			last if ($tot - $i) < 4;
19584
19585			my $head = substr($byte, $i, 4) || last;
19586
19587			next if (ord($head) != 0xff);
19588
19589			$h = _get_head($head);
19590			$is_mp3 = _is_mp3($h);
19591			last if $is_mp3;
19592		}
19593
19594		# adjust where we are for _get_vbr()
19595		$off += $i;
19596
19597		if ($off > $tot && !$try_harder) {
19598			_close($file, $fh);
19599			$@ = "Couldn't find MP3 header (perhaps set " .
19600			     '$MP3::Info::try_harder and retry)';
19601			return undef;
19602		}
19603	}
19604
19605	my $vbr = _get_vbr($fh, $h, \$off);
19606
19607	seek $fh, 0, 2;
19608	$eof = tell $fh;
19609	seek $fh, -128, 2;
19610	$eof -= 128 if <$fh> =~ /^TAG/ ? 1 : 0;
19611
19612	_close($file, $fh);
19613
19614	$h->{size} = $eof - $off;
19615	$h->{offset} = $off;
19616
19617	return _get_info($h, $vbr);
19618}
19619
19620sub _get_info {
19621	my($h, $vbr) = @_;
19622	my $i;
19623
19624	# No bitrate or sample rate? Something's wrong.
19625	unless ($h->{bitrate} && $h->{fs}) {
19626		return {};
19627	}
19628
19629	$i->{VERSION}	= $h->{IDR} == 2 ? 2 : $h->{IDR} == 3 ? 1 :
19630				$h->{IDR} == 0 ? 2.5 : 0;
19631	$i->{LAYER}	= 4 - $h->{layer};
19632	$i->{VBR}	= defined $vbr ? 1 : 0;
19633
19634	$i->{COPYRIGHT}	= $h->{copyright} ? 1 : 0;
19635	$i->{PADDING}	= $h->{padding_bit} ? 1 : 0;
19636	$i->{STEREO}	= $h->{mode} == 3 ? 0 : 1;
19637	$i->{MODE}	= $h->{mode};
19638
19639	$i->{SIZE}	= $vbr && $vbr->{bytes} ? $vbr->{bytes} : $h->{size};
19640	$i->{OFFSET}	= $h->{offset};
19641
19642	my $mfs		= $h->{fs} / ($h->{ID} ? 144000 : 72000);
19643	$i->{FRAMES}	= int($vbr && $vbr->{frames}
19644				? $vbr->{frames}
19645				: $i->{SIZE} / ($h->{bitrate} / $mfs)
19646			  );
19647
19648	if ($vbr) {
19649		$i->{VBR_SCALE}	= $vbr->{scale} if $vbr->{scale};
19650		$h->{bitrate}	= $i->{SIZE} / $i->{FRAMES} * $mfs;
19651		if (not $h->{bitrate}) {
19652			$@ = "Couldn't determine VBR bitrate";
19653			return undef;
19654		}
19655	}
19656
19657	$h->{'length'}	= ($i->{SIZE} * 8) / $h->{bitrate} / 10;
19658	$i->{SECS}	= $h->{'length'} / 100;
19659	$i->{MM}	= int $i->{SECS} / 60;
19660	$i->{SS}	= int $i->{SECS} % 60;
19661	$i->{MS}	= (($i->{SECS} - ($i->{MM} * 60) - $i->{SS}) * 1000);
19662#	$i->{LF}	= ($i->{MS} / 1000) * ($i->{FRAMES} / $i->{SECS});
19663#	int($i->{MS} / 100 * 75);  # is this right?
19664	$i->{TIME}	= sprintf "%.2d:%.2d", @{$i}{'MM', 'SS'};
19665
19666	$i->{BITRATE}		= int $h->{bitrate};
19667	# should we just return if ! FRAMES?
19668	$i->{FRAME_LENGTH}	= int($h->{size} / $i->{FRAMES}) if $i->{FRAMES};
19669	$i->{FREQUENCY}		= $frequency_tbl[3 * $h->{IDR} + $h->{sampling_freq}];
19670
19671	return $i;
19672}
19673
19674sub _get_head {
19675	my($byte) = @_;
19676	my($bytes, $h);
19677
19678	$bytes = _unpack_head($byte);
19679	@$h{qw(IDR ID layer protection_bit
19680		bitrate_index sampling_freq padding_bit private_bit
19681		mode mode_extension copyright original
19682		emphasis version_index bytes)} = (
19683		($bytes>>19)&3, ($bytes>>19)&1, ($bytes>>17)&3, ($bytes>>16)&1,
19684		($bytes>>12)&15, ($bytes>>10)&3, ($bytes>>9)&1, ($bytes>>8)&1,
19685		($bytes>>6)&3, ($bytes>>4)&3, ($bytes>>3)&1, ($bytes>>2)&1,
19686		$bytes&3, ($bytes>>19)&3, $bytes
19687	);
19688
19689	$h->{bitrate} = $t_bitrate[$h->{ID}][3 - $h->{layer}][$h->{bitrate_index}];
19690	$h->{fs} = $t_sampling_freq[$h->{IDR}][$h->{sampling_freq}];
19691
19692	return $h;
19693}
19694
19695sub _is_mp3 {
19696	my $h = $_[0] or return undef;
19697	return ! (	# all below must be false
19698		 $h->{bitrate_index} == 0
19699			||
19700		 $h->{version_index} == 1
19701			||
19702		($h->{bytes} & 0xFFE00000) != 0xFFE00000
19703			||
19704		!$h->{fs}
19705			||
19706		!$h->{bitrate}
19707			||
19708		 $h->{bitrate_index} == 15
19709			||
19710		!$h->{layer}
19711			||
19712		 $h->{sampling_freq} == 3
19713			||
19714		 $h->{emphasis} == 2
19715			||
19716		!$h->{bitrate_index}
19717			||
19718		($h->{bytes} & 0xFFFF0000) == 0xFFFE0000
19719			||
19720		($h->{ID} == 1 && $h->{layer} == 3 && $h->{protection_bit} == 1)
19721		# mode extension should only be applicable when mode = 1
19722		# however, failing just becuase mode extension is used when unneeded is a bit strict
19723		#	||
19724		#($h->{mode_extension} != 0 && $h->{mode} != 1)
19725	);
19726}
19727
19728sub _vbr_seek {
19729	my $fh    = shift;
19730	my $off   = shift;
19731	my $bytes = shift;
19732	my $n     = shift || 4;
19733
19734	seek $fh, $$off, 0;
19735	read $fh, $$bytes, $n;
19736
19737	$$off += $n;
19738}
19739
19740sub _get_vbr {
19741	my($fh, $h, $roff) = @_;
19742	my($off, $bytes, @bytes, %vbr);
19743
19744	$off = $$roff;
19745
19746	$off += 4;
19747
19748	if ($h->{ID}) {	# MPEG1
19749		$off += $h->{mode} == 3 ? 17 : 32;
19750	} else {	# MPEG2
19751		$off += $h->{mode} == 3 ? 9 : 17;
19752	}
19753
19754	_vbr_seek($fh, \$off, \$bytes);
19755	return unless $bytes eq 'Xing';
19756
19757	_vbr_seek($fh, \$off, \$bytes);
19758	$vbr{flags} = _unpack_head($bytes);
19759
19760	if ($vbr{flags} & 1) {
19761		_vbr_seek($fh, \$off, \$bytes);
19762		$vbr{frames} = _unpack_head($bytes);
19763	}
19764
19765	if ($vbr{flags} & 2) {
19766		_vbr_seek($fh, \$off, \$bytes);
19767		$vbr{bytes} = _unpack_head($bytes);
19768	}
19769
19770	if ($vbr{flags} & 4) {
19771		_vbr_seek($fh, \$off, \$bytes, 100);
19772# Not used right now ...
19773#		$vbr{toc} = _unpack_head($bytes);
19774	}
19775
19776	if ($vbr{flags} & 8) { # (quality ind., 0=best 100=worst)
19777		_vbr_seek($fh, \$off, \$bytes);
19778		$vbr{scale} = _unpack_head($bytes);
19779	} else {
19780		$vbr{scale} = -1;
19781	}
19782
19783	$$roff = $off;
19784	return \%vbr;
19785}
19786
19787sub _get_v2head {
19788	my $fh = $_[0] or return;
19789	my($v2h, $bytes, @bytes);
19790	$v2h->{offset} = 0;
19791
19792	# check first three bytes for 'ID3'
19793	seek $fh, 0, 0;
19794	read $fh, $bytes, 3;
19795
19796	# TODO: add support for tags at the end of the file
19797	if ($bytes eq 'RIF' || $bytes eq 'FOR') {
19798		_find_id3_chunk($fh, $bytes) or return;
19799		$v2h->{offset} = tell $fh;
19800		read $fh, $bytes, 3;
19801	}
19802
19803	return unless $bytes eq 'ID3';
19804
19805	# get version
19806	read $fh, $bytes, 2;
19807	$v2h->{version} = sprintf "ID3v2.%d.%d",
19808		@$v2h{qw[major_version minor_version]} =
19809			unpack 'c2', $bytes;
19810
19811	# get flags
19812	read $fh, $bytes, 1;
19813	my @bits = split //, unpack 'b8', $bytes;
19814	if ($v2h->{major_version} == 2) {
19815		$v2h->{unsync}       = $bits[7];
19816		$v2h->{compression}  = $bits[8];
19817		$v2h->{ext_header}   = 0;
19818		$v2h->{experimental} = 0;
19819	} else {
19820		$v2h->{unsync}       = $bits[7];
19821		$v2h->{ext_header}   = $bits[6];
19822		$v2h->{experimental} = $bits[5];
19823		$v2h->{footer}       = $bits[4] if $v2h->{major_version} == 4;
19824	}
19825
19826	# get ID3v2 tag length from bytes 7-10
19827	$v2h->{tag_size} = 10;	# include ID3v2 header size
19828	$v2h->{tag_size} += 10 if $v2h->{footer};
19829	read $fh, $bytes, 4;
19830	@bytes = reverse unpack 'C4', $bytes;
19831	foreach my $i (0 .. 3) {
19832		# whoaaaaaa nellllllyyyyyy!
19833		$v2h->{tag_size} += $bytes[$i] * 128 ** $i;
19834	}
19835
19836	# get extended header size
19837	$v2h->{ext_header_size} = 0;
19838	if ($v2h->{ext_header}) {
19839		read $fh, $bytes, 4;
19840		@bytes = reverse unpack 'C4', $bytes;
19841
19842		# use syncsafe bytes if using version 2.4
19843		my $bytesize = ($v2h->{major_version} > 3) ? 128 : 256;
19844		for my $i (0..3) {
19845			$v2h->{ext_header_size} += $bytes[$i] * $bytesize ** $i;
19846		}
19847	}
19848
19849	return $v2h;
19850}
19851
19852sub _find_id3_chunk {
19853	my($fh, $filetype) = @_;
19854	my($bytes, $size, $tag, $pat, $mat);
19855
19856	read $fh, $bytes, 1;
19857	if ($filetype eq 'RIF') {  # WAV
19858		return 0 if $bytes ne 'F';
19859		$pat = 'a4V';
19860		$mat = 'id3 ';
19861	} elsif ($filetype eq 'FOR') { # AIFF
19862		return 0 if $bytes ne 'M';
19863		$pat = 'a4N';
19864		$mat = 'ID3 ';
19865	}
19866	seek $fh, 12, 0;  # skip to the first chunk
19867
19868	while ((read $fh, $bytes, 8) == 8) {
19869		($tag, $size)  = unpack $pat, $bytes;
19870		return 1 if $tag eq $mat;
19871		seek $fh, $size, 1;
19872	}
19873
19874	return 0;
19875}
19876
19877sub _unpack_head {
19878	unpack('l', pack('L', unpack('N', $_[0])));
19879}
19880
19881sub _grab_int_16 {
19882        my $data  = shift;
19883        my $value = unpack('s',substr($$data,0,2));
19884        $$data    = substr($$data,2);
19885        return $value;
19886}
19887
19888sub _grab_uint_16 {
19889        my $data  = shift;
19890        my $value = unpack('S',substr($$data,0,2));
19891        $$data    = substr($$data,2);
19892        return $value;
19893}
19894
19895sub _grab_int_32 {
19896        my $data  = shift;
19897        my $value = unpack('V',substr($$data,0,4));
19898        $$data    = substr($$data,4);
19899        return $value;
19900}
19901
19902sub _parse_ape_tag {
19903	my ($fh, $filesize, $info) = @_;
19904
19905	my $ape_tag_id = 'APETAGEX';
19906
19907	seek $fh, -256, 2;
19908	read($fh, my $tag, 256);
19909	my $pre_tag = substr($tag, 0, 128, '');
19910
19911	# Try and bail early if there's no ape tag.
19912	if (substr($pre_tag, 96, 8) ne $ape_tag_id && substr($tag, 96, 8) ne $ape_tag_id) {
19913
19914		seek($fh, 0, 0);
19915		return 0;
19916	}
19917
19918	my $id3v1_tag_size      = 128;
19919	my $ape_tag_header_size = 32;
19920	my $lyrics3_tag_size    = 10;
19921	my $tag_offset_start    = 0;
19922	my $tag_offset_end      = 0;
19923
19924	seek($fh, (0 - $id3v1_tag_size - $ape_tag_header_size - $lyrics3_tag_size), 2);
19925
19926	read($fh, my $ape_footer_id3v1, $id3v1_tag_size + $ape_tag_header_size + $lyrics3_tag_size);
19927
19928	if (substr($ape_footer_id3v1, (length($ape_footer_id3v1) - $id3v1_tag_size - $ape_tag_header_size), 8) eq $ape_tag_id) {
19929
19930		$tag_offset_end = $filesize - $id3v1_tag_size;
19931
19932	} elsif (substr($ape_footer_id3v1, (length($ape_footer_id3v1) - $ape_tag_header_size), 8) eq $ape_tag_id) {
19933
19934		$tag_offset_end = $filesize;
19935	}
19936
19937	seek($fh, $tag_offset_end - $ape_tag_header_size, 0);
19938
19939	read($fh, my $ape_footer_data, 32);
19940
19941	my $ape_footer = _parse_ape_header_or_footer($ape_footer_data);
19942
19943	if (keys %{$ape_footer}) {
19944
19945		my $ape_tag_data = '';
19946
19947		if ($ape_footer->{'flags'}->{'header'}) {
19948
19949			seek($fh, ($tag_offset_end - $ape_footer->{'tag_size'} - $ape_tag_header_size), 0);
19950
19951			$tag_offset_start = tell($fh);
19952
19953			read($fh, $ape_tag_data, $ape_footer->{'tag_size'} + $ape_tag_header_size);
19954
19955		} else {
19956
19957			$tag_offset_start = $tag_offset_end - $ape_footer->{'tag_size'};
19958
19959			seek($fh, $tag_offset_start, 0);
19960
19961			read($fh, $ape_tag_data, $ape_footer->{'tag_size'});
19962		}
19963
19964		my $ape_header_data = substr($ape_tag_data, 0, $ape_tag_header_size, '');
19965		my $ape_header      = _parse_ape_header_or_footer($ape_header_data);
19966
19967		for (my $c = 0; $c < $ape_header->{'tag_items'}; $c++) {
19968
19969			# Loop through the tag items
19970			my $tag_len   = _grab_int_32(\$ape_tag_data);
19971			my $tag_flags = _grab_int_32(\$ape_tag_data);
19972
19973			$ape_tag_data =~ s/^(.*?)\0//;
19974
19975			my $tag_item_key = uc($1 || 'UNKNOWN');
19976
19977			$info->{$tag_item_key} = substr($ape_tag_data, 0, $tag_len, '');
19978		}
19979	}
19980
19981	seek($fh, 0, 0);
19982
19983	return 1;
19984}
19985
19986sub _parse_ape_header_or_footer {
19987	my $bytes = shift;
19988	my %data = ();
19989
19990	if (substr($bytes, 0, 8, '') eq 'APETAGEX') {
19991
19992		$data{'version'}      = _grab_int_32(\$bytes);
19993		$data{'tag_size'}     = _grab_int_32(\$bytes);
19994		$data{'tag_items'}    = _grab_int_32(\$bytes);
19995		$data{'global_flags'} = _grab_int_32(\$bytes);
19996
19997		# trim the reseved bytes
19998		_grab_int_32(\$bytes);
19999		_grab_int_32(\$bytes);
20000
20001		$data{'flags'}->{'header'}    = ($data{'global_flags'} & 0x80000000) ? 1 : 0;
20002		$data{'flags'}->{'footer'}    = ($data{'global_flags'} & 0x40000000) ? 1 : 0;
20003		$data{'flags'}->{'is_header'} = ($data{'global_flags'} & 0x20000000) ? 1 : 0;
20004	}
20005
20006	return \%data;
20007}
20008
20009sub _close {
20010	my($file, $fh) = @_;
20011	unless (ref $file) { # filehandle not passed
20012		close $fh or carp "Problem closing '$file': $!";
20013	}
20014}
20015
20016BEGIN {
20017	@mp3_genres = (
20018		'Blues',
20019		'Classic Rock',
20020		'Country',
20021		'Dance',
20022		'Disco',
20023		'Funk',
20024		'Grunge',
20025		'Hip-Hop',
20026		'Jazz',
20027		'Metal',
20028		'New Age',
20029		'Oldies',
20030		'Other',
20031		'Pop',
20032		'R&B',
20033		'Rap',
20034		'Reggae',
20035		'Rock',
20036		'Techno',
20037		'Industrial',
20038		'Alternative',
20039		'Ska',
20040		'Death Metal',
20041		'Pranks',
20042		'Soundtrack',
20043		'Euro-Techno',
20044		'Ambient',
20045		'Trip-Hop',
20046		'Vocal',
20047		'Jazz+Funk',
20048		'Fusion',
20049		'Trance',
20050		'Classical',
20051		'Instrumental',
20052		'Acid',
20053		'House',
20054		'Game',
20055		'Sound Clip',
20056		'Gospel',
20057		'Noise',
20058		'AlternRock',
20059		'Bass',
20060		'Soul',
20061		'Punk',
20062		'Space',
20063		'Meditative',
20064		'Instrumental Pop',
20065		'Instrumental Rock',
20066		'Ethnic',
20067		'Gothic',
20068		'Darkwave',
20069		'Techno-Industrial',
20070		'Electronic',
20071		'Pop-Folk',
20072		'Eurodance',
20073		'Dream',
20074		'Southern Rock',
20075		'Comedy',
20076		'Cult',
20077		'Gangsta',
20078		'Top 40',
20079		'Christian Rap',
20080		'Pop/Funk',
20081		'Jungle',
20082		'Native American',
20083		'Cabaret',
20084		'New Wave',
20085		'Psychadelic',
20086		'Rave',
20087		'Showtunes',
20088		'Trailer',
20089		'Lo-Fi',
20090		'Tribal',
20091		'Acid Punk',
20092		'Acid Jazz',
20093		'Polka',
20094		'Retro',
20095		'Musical',
20096		'Rock & Roll',
20097		'Hard Rock',
20098	);
20099
20100	@winamp_genres = (
20101		@mp3_genres,
20102		'Folk',
20103		'Folk-Rock',
20104		'National Folk',
20105		'Swing',
20106		'Fast Fusion',
20107		'Bebop',
20108		'Latin',
20109		'Revival',
20110		'Celtic',
20111		'Bluegrass',
20112		'Avantgarde',
20113		'Gothic Rock',
20114		'Progressive Rock',
20115		'Psychedelic Rock',
20116		'Symphonic Rock',
20117		'Slow Rock',
20118		'Big Band',
20119		'Chorus',
20120		'Easy Listening',
20121		'Acoustic',
20122		'Humour',
20123		'Speech',
20124		'Chanson',
20125		'Opera',
20126		'Chamber Music',
20127		'Sonata',
20128		'Symphony',
20129		'Booty Bass',
20130		'Primus',
20131		'Porn Groove',
20132		'Satire',
20133		'Slow Jam',
20134		'Club',
20135		'Tango',
20136		'Samba',
20137		'Folklore',
20138		'Ballad',
20139		'Power Ballad',
20140		'Rhythmic Soul',
20141		'Freestyle',
20142		'Duet',
20143		'Punk Rock',
20144		'Drum Solo',
20145		'Acapella',
20146		'Euro-House',
20147		'Dance Hall',
20148		'Goa',
20149		'Drum & Bass',
20150		'Club-House',
20151		'Hardcore',
20152		'Terror',
20153		'Indie',
20154		'BritPop',
20155		'Negerpunk',
20156		'Polsk Punk',
20157		'Beat',
20158		'Christian Gangsta Rap',
20159		'Heavy Metal',
20160		'Black Metal',
20161		'Crossover',
20162		'Contemporary Christian',
20163		'Christian Rock',
20164		'Merengue',
20165		'Salsa',
20166		'Thrash Metal',
20167		'Anime',
20168		'JPop',
20169		'Synthpop',
20170	);
20171
20172	@t_bitrate = ([
20173		[0, 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256],
20174		[0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160],
20175		[0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160]
20176	],[
20177		[0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448],
20178		[0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384],
20179		[0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320]
20180	]);
20181
20182	@t_sampling_freq = (
20183		[11025, 12000,  8000],
20184		[undef, undef, undef],	# reserved
20185		[22050, 24000, 16000],
20186		[44100, 48000, 32000]
20187	);
20188
20189	@frequency_tbl = map { $_ ? eval "${_}e-3" : 0 }
20190		map { @$_ } @t_sampling_freq;
20191
20192	@mp3_info_fields = qw(
20193		VERSION
20194		LAYER
20195		STEREO
20196		VBR
20197		BITRATE
20198		FREQUENCY
20199		SIZE
20200		OFFSET
20201		SECS
20202		MM
20203		SS
20204		MS
20205		TIME
20206		COPYRIGHT
20207		PADDING
20208		MODE
20209		FRAMES
20210		FRAME_LENGTH
20211		VBR_SCALE
20212	);
20213
20214	%rva2_channel_types = (
20215		0x00 => 'OTHER',
20216		0x01 => 'MASTER',
20217		0x02 => 'FRONT_RIGHT',
20218		0x03 => 'FRONT_LEFT',
20219		0x04 => 'BACK_RIGHT',
20220		0x05 => 'BACK_LEFT',
20221		0x06 => 'FRONT_CENTER',
20222		0x07 => 'BACK_CENTER',
20223		0x08 => 'SUBWOOFER',
20224	);
20225
20226	%v1_tag_fields =
20227		(TITLE => 30, ARTIST => 30, ALBUM => 30, COMMENT => 30, YEAR => 4);
20228
20229	@v1_tag_names = qw(TITLE ARTIST ALBUM YEAR COMMENT TRACKNUM GENRE);
20230
20231	%v2_to_v1_names = (
20232		# v2.2 tags
20233		'TT2' => 'TITLE',
20234		'TP1' => 'ARTIST',
20235		'TAL' => 'ALBUM',
20236		'TYE' => 'YEAR',
20237		'COM' => 'COMMENT',
20238		'TRK' => 'TRACKNUM',
20239		'TCO' => 'GENRE', # not clean mapping, but ...
20240		# v2.3 tags
20241		'TIT2' => 'TITLE',
20242		'TPE1' => 'ARTIST',
20243		'TALB' => 'ALBUM',
20244		'TYER' => 'YEAR',
20245		'COMM' => 'COMMENT',
20246		'TRCK' => 'TRACKNUM',
20247		'TCON' => 'GENRE',
20248		# v2.3 tags - needed for MusicBrainz
20249		'UFID' => 'Unique file identifier',
20250		'TXXX' => 'User defined text information frame',
20251	);
20252
20253	%v2_tag_names = (
20254		# v2.2 tags
20255		'BUF' => 'Recommended buffer size',
20256		'CNT' => 'Play counter',
20257		'COM' => 'Comments',
20258		'CRA' => 'Audio encryption',
20259		'CRM' => 'Encrypted meta frame',
20260		'ETC' => 'Event timing codes',
20261		'EQU' => 'Equalization',
20262		'GEO' => 'General encapsulated object',
20263		'IPL' => 'Involved people list',
20264		'LNK' => 'Linked information',
20265		'MCI' => 'Music CD Identifier',
20266		'MLL' => 'MPEG location lookup table',
20267		'PIC' => 'Attached picture',
20268		'POP' => 'Popularimeter',
20269		'REV' => 'Reverb',
20270		'RVA' => 'Relative volume adjustment',
20271		'SLT' => 'Synchronized lyric/text',
20272		'STC' => 'Synced tempo codes',
20273		'TAL' => 'Album/Movie/Show title',
20274		'TBP' => 'BPM (Beats Per Minute)',
20275		'TCM' => 'Composer',
20276		'TCO' => 'Content type',
20277		'TCR' => 'Copyright message',
20278		'TDA' => 'Date',
20279		'TDY' => 'Playlist delay',
20280		'TEN' => 'Encoded by',
20281		'TFT' => 'File type',
20282		'TIM' => 'Time',
20283		'TKE' => 'Initial key',
20284		'TLA' => 'Language(s)',
20285		'TLE' => 'Length',
20286		'TMT' => 'Media type',
20287		'TOA' => 'Original artist(s)/performer(s)',
20288		'TOF' => 'Original filename',
20289		'TOL' => 'Original Lyricist(s)/text writer(s)',
20290		'TOR' => 'Original release year',
20291		'TOT' => 'Original album/Movie/Show title',
20292		'TP1' => 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group',
20293		'TP2' => 'Band/Orchestra/Accompaniment',
20294		'TP3' => 'Conductor/Performer refinement',
20295		'TP4' => 'Interpreted, remixed, or otherwise modified by',
20296		'TPA' => 'Part of a set',
20297		'TPB' => 'Publisher',
20298		'TRC' => 'ISRC (International Standard Recording Code)',
20299		'TRD' => 'Recording dates',
20300		'TRK' => 'Track number/Position in set',
20301		'TSI' => 'Size',
20302		'TSS' => 'Software/hardware and settings used for encoding',
20303		'TT1' => 'Content group description',
20304		'TT2' => 'Title/Songname/Content description',
20305		'TT3' => 'Subtitle/Description refinement',
20306		'TXT' => 'Lyricist/text writer',
20307		'TXX' => 'User defined text information frame',
20308		'TYE' => 'Year',
20309		'UFI' => 'Unique file identifier',
20310		'ULT' => 'Unsychronized lyric/text transcription',
20311		'WAF' => 'Official audio file webpage',
20312		'WAR' => 'Official artist/performer webpage',
20313		'WAS' => 'Official audio source webpage',
20314		'WCM' => 'Commercial information',
20315		'WCP' => 'Copyright/Legal information',
20316		'WPB' => 'Publishers official webpage',
20317		'WXX' => 'User defined URL link frame',
20318
20319		# v2.3 tags
20320		'AENC' => 'Audio encryption',
20321		'APIC' => 'Attached picture',
20322		'COMM' => 'Comments',
20323		'COMR' => 'Commercial frame',
20324		'ENCR' => 'Encryption method registration',
20325		'EQUA' => 'Equalization',
20326		'ETCO' => 'Event timing codes',
20327		'GEOB' => 'General encapsulated object',
20328		'GRID' => 'Group identification registration',
20329		'IPLS' => 'Involved people list',
20330		'LINK' => 'Linked information',
20331		'MCDI' => 'Music CD identifier',
20332		'MLLT' => 'MPEG location lookup table',
20333		'OWNE' => 'Ownership frame',
20334		'PCNT' => 'Play counter',
20335		'POPM' => 'Popularimeter',
20336		'POSS' => 'Position synchronisation frame',
20337		'PRIV' => 'Private frame',
20338		'RBUF' => 'Recommended buffer size',
20339		'RVAD' => 'Relative volume adjustment',
20340		'RVRB' => 'Reverb',
20341		'SYLT' => 'Synchronized lyric/text',
20342		'SYTC' => 'Synchronized tempo codes',
20343		'TALB' => 'Album/Movie/Show title',
20344		'TBPM' => 'BPM (beats per minute)',
20345		'TCOM' => 'Composer',
20346		'TCON' => 'Content type',
20347		'TCOP' => 'Copyright message',
20348		'TDAT' => 'Date',
20349		'TDLY' => 'Playlist delay',
20350		'TENC' => 'Encoded by',
20351		'TEXT' => 'Lyricist/Text writer',
20352		'TFLT' => 'File type',
20353		'TIME' => 'Time',
20354		'TIT1' => 'Content group description',
20355		'TIT2' => 'Title/songname/content description',
20356		'TIT3' => 'Subtitle/Description refinement',
20357		'TKEY' => 'Initial key',
20358		'TLAN' => 'Language(s)',
20359		'TLEN' => 'Length',
20360		'TMED' => 'Media type',
20361		'TOAL' => 'Original album/movie/show title',
20362		'TOFN' => 'Original filename',
20363		'TOLY' => 'Original lyricist(s)/text writer(s)',
20364		'TOPE' => 'Original artist(s)/performer(s)',
20365		'TORY' => 'Original release year',
20366		'TOWN' => 'File owner/licensee',
20367		'TPE1' => 'Lead performer(s)/Soloist(s)',
20368		'TPE2' => 'Band/orchestra/accompaniment',
20369		'TPE3' => 'Conductor/performer refinement',
20370		'TPE4' => 'Interpreted, remixed, or otherwise modified by',
20371		'TPOS' => 'Part of a set',
20372		'TPUB' => 'Publisher',
20373		'TRCK' => 'Track number/Position in set',
20374		'TRDA' => 'Recording dates',
20375		'TRSN' => 'Internet radio station name',
20376		'TRSO' => 'Internet radio station owner',
20377		'TSIZ' => 'Size',
20378		'TSRC' => 'ISRC (international standard recording code)',
20379		'TSSE' => 'Software/Hardware and settings used for encoding',
20380		'TXXX' => 'User defined text information frame',
20381		'TYER' => 'Year',
20382		'UFID' => 'Unique file identifier',
20383		'USER' => 'Terms of use',
20384		'USLT' => 'Unsychronized lyric/text transcription',
20385		'WCOM' => 'Commercial information',
20386		'WCOP' => 'Copyright/Legal information',
20387		'WOAF' => 'Official audio file webpage',
20388		'WOAR' => 'Official artist/performer webpage',
20389		'WOAS' => 'Official audio source webpage',
20390		'WORS' => 'Official internet radio station homepage',
20391		'WPAY' => 'Payment',
20392		'WPUB' => 'Publishers official webpage',
20393		'WXXX' => 'User defined URL link frame',
20394
20395		# v2.4 additional tags
20396		# note that we don't restrict tags from 2.3 or 2.4,
20397		'ASPI' => 'Audio seek point index',
20398		'EQU2' => 'Equalisation (2)',
20399		'RVA2' => 'Relative volume adjustment (2)',
20400		'SEEK' => 'Seek frame',
20401		'SIGN' => 'Signature frame',
20402		'TDEN' => 'Encoding time',
20403		'TDOR' => 'Original release time',
20404		'TDRC' => 'Recording time',
20405		'TDRL' => 'Release time',
20406		'TDTG' => 'Tagging time',
20407		'TIPL' => 'Involved people list',
20408		'TMCL' => 'Musician credits list',
20409		'TMOO' => 'Mood',
20410		'TPRO' => 'Produced notice',
20411		'TSOA' => 'Album sort order',
20412		'TSOP' => 'Performer sort order',
20413		'TSOT' => 'Title sort order',
20414		'TSST' => 'Set subtitle',
20415
20416		# grrrrrrr
20417		'COM ' => 'Broken iTunes comments',
20418	);
20419}
20420
20421=pod
20422
20423=back
20424
20425=head1 TROUBLESHOOTING
20426
20427If you find a bug, please send me a patch (see the project page in L<"SEE ALSO">).
20428If you cannot figure out why it does not work for you, please put the MP3 file in
20429a place where I can get it (preferably via FTP, or HTTP, or .Mac iDisk) and send me
20430mail regarding where I can get the file, with a detailed description of the problem.
20431
20432If I download the file, after debugging the problem I will not keep the MP3 file
20433if it is not legal for me to have it.  Just let me know if it is legal for me to
20434keep it or not.
20435
20436
20437=head1 TODO
20438
20439=over 4
20440
20441=item ID3v2 Support
20442
20443Still need to do more for reading tags, such as using Compress::Zlib to decompress
20444compressed tags.  But until I see this in use more, I won't bother.  If something
20445does not work properly with reading, follow the instructions above for
20446troubleshooting.
20447
20448ID3v2 I<writing> is coming soon.
20449
20450=item Get data from scalar
20451
20452Instead of passing a file spec or filehandle, pass the
20453data itself.  Would take some work, converting the seeks, etc.
20454
20455=item Padding bit ?
20456
20457Do something with padding bit.
20458
20459=item Test suite
20460
20461Test suite could use a bit of an overhaul and update.  Patches very welcome.
20462
20463=over 4
20464
20465=item *
20466
20467Revamp getset.t.  Test all the various get_mp3tag args.
20468
20469=item *
20470
20471Test Unicode.
20472
20473=item *
20474
20475Test OOP API.
20476
20477=item *
20478
20479Test error handling, check more for missing files, bad MP3s, etc.
20480
20481=back
20482
20483=item Other VBR
20484
20485Right now, only Xing VBR is supported.
20486
20487=back
20488
20489
20490=head1 THANKS
20491
20492Edward Allen,
20493Vittorio Bertola,
20494Michael Blakeley,
20495Per Bolmstedt,
20496Tony Bowden,
20497Tom Brown,
20498Sergio Camarena,
20499Chris Dawson,
20500Anthony DiSante,
20501Luke Drumm,
20502Kyle Farrell,
20503Jeffrey Friedl,
20504brian d foy,
20505Ben Gertzfield,
20506Brian Goodwin,
20507Todd Hanneken,
20508Todd Harris,
20509Woodrow Hill,
20510Kee Hinckley,
20511Roman Hodek,
20512Ilya Konstantinov,
20513Peter Kovacs,
20514Johann Lindvall,
20515Alex Marandon,
20516Peter Marschall,
20517michael,
20518Trond Michelsen,
20519Dave O'Neill,
20520Christoph Oberauer,
20521Jake Palmer,
20522Andrew Phillips,
20523David Reuteler,
20524John Ruttenberg,
20525Matthew Sachs,
20526scfc_de,
20527Hermann Schwaerzler,
20528Chris Sidi,
20529Roland Steinbach,
20530Brian S. Stephan,
20531Stuart,
20532Dan Sully,
20533Jeffery Sumler,
20534Predrag Supurovic,
20535Bogdan Surdu,
20536Pierre-Yves Thoulon,
20537tim,
20538Pass F. B. Travis,
20539Tobias Wagener,
20540Ronan Waide,
20541Andy Waite,
20542Ken Williams,
20543Ben Winslow,
20544Meng Weng Wong.
20545
20546
20547=head1 CURRENT AUTHOR
20548
20549Dan Sully E<lt>dan | at | slimdevices.comE<gt> & Slim Devices, Inc.
20550
20551=head1 AUTHOR EMERITUS
20552
20553Chris Nandor E<lt>pudge@pobox.comE<gt>, http://pudge.net/
20554
20555=head1 COPYRIGHT AND LICENSE
20556
20557Copyright (c) 2006 Dan Sully & Slim Devices, Inc. All rights reserved.
20558
20559Copyright (c) 1998-2005 Chris Nandor. All rights reserved.
20560
20561This program is free software; you can redistribute it and/or modify it under
20562the same terms as Perl itself.
20563
20564=head1 SEE ALSO
20565
20566=over 4
20567
20568=item Slim Devices
20569
20570	http://www.slimdevices.com/
20571
20572=item mp3tools
20573
20574	http://www.zevils.com/linux/mp3tools/
20575
20576=item mpgtools
20577
20578	http://www.dv.co.yu/mpgscript/mpgtools.htm
20579	http://www.dv.co.yu/mpgscript/mpeghdr.htm
20580
20581=item mp3tool
20582
20583	http://www.dtek.chalmers.se/~d2linjo/mp3/mp3tool.html
20584
20585=item ID3v2
20586
20587	http://www.id3.org/
20588
20589=item Xing Variable Bitrate
20590
20591	http://www.xingtech.com/support/partner_developer/mp3/vbr_sdk/
20592
20593=item MP3Ext
20594
20595	http://rupert.informatik.uni-stuttgart.de/~mutschml/MP3ext/
20596
20597=item Xmms
20598
20599	http://www.xmms.org/
20600
20601
20602=back
20603
20604=cut
20605
20606
20607
20608
206091;
20610} # end scope block
20611
20612########################-----CDDB_get-----########################
20613
20614{ # start scope block
20615#  (c) 2004 Armin Obersteiner <armin@xos.net>
20616package CDDB_get::Internal;
20617
20618use Config;
20619
20620use strict;
20621use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $debug);
20622
20623require Exporter;
20624
20625@ISA = qw(Exporter AutoLoader);
20626# Items to export into callers namespace by default. Note: do not export
20627# names by default without a very good reason. Use EXPORT_OK instead.
20628# Do not simply export all your public functions/methods/constants.
20629@EXPORT_OK = qw(
20630  get_cddb
20631  get_discids
20632);
20633$VERSION = '2.27';
20634
20635use Fcntl;
20636use IO::Socket;
20637use Data::Dumper qw(Dumper);
20638
20639#$debug=1;
20640
20641# setup for linux, solaris x86, solaris spark
20642# you freebsd guys give me input
20643
20644# listed variables to be "local" to the enclosing block. See 'man perlsub'.
20645my ( $os, $machine, $CDROMREADTOCHDR, $CDROMREADTOCENTRY, $CDROM_MSF );
20646my ( $CDDB_HOST, $CDDB_PORT, $CDDB_MODE, $CD_DEVICE   );
20647my ( $HELLO_ID, $PROTO_VERSION, $BIG_ENDIAN, $BITS_64 );
20648
20649sub set_standard_values {
20650
20651   print STDERR "cddb: checking for os ... " if $debug;
20652
20653   $os=`uname -s`;
20654   $machine=`uname -m`;
20655   chomp $os;
20656   chomp $machine;
20657
20658   print STDERR "$os ($machine) " if $debug;
20659
20660   # cdrom IOCTL magic (from c headers)
20661   # linux x86 is default
20662
20663   # /usr/include/linux/cdrom.h
20664   $CDROMREADTOCHDR=0x5305;
20665   $CDROMREADTOCENTRY=0x5306;
20666   $CDROM_MSF=0x02;
20667
20668   # default config
20669
20670   $CDDB_HOST = "freedb.freedb.org";
20671   $CDDB_PORT = 8880;
20672   $CDDB_MODE = "cddb";
20673   $CD_DEVICE = "/dev/cdrom";
20674
20675   $HELLO_ID  = "root nowhere.com fastrip 0.77";
20676   $PROTO_VERSION = 5;
20677
20678   # endian check
20679
20680   $BIG_ENDIAN = unpack("h*", pack("s", 1)) =~ /01/;
20681
20682   if($BIG_ENDIAN) {
20683      print STDERR "[big endian] " if $debug;
20684   } else {
20685      print STDERR "[little endian] " if $debug;
20686   }
20687
20688   # 64bit pointer check
20689
20690   $BITS_64 = $Config{ptrsize} == 8 ? 1 : 0;
20691
20692   if($BITS_64) {
20693      print STDERR "[64 bit]\n" if $debug;
20694   } else {
20695      print STDERR "[32 bit]\n" if $debug;
20696   }
20697
20698   if($os eq "SunOS") {
20699      # /usr/include/sys/cdio.h
20700      $CDROMREADTOCHDR=0x49b;	# 1179
20701      $CDROMREADTOCENTRY=0x49c;	# 1180
20702
20703      if(-e "/vol/dev/aliases/cdrom0") {
20704         $CD_DEVICE="/vol/dev/aliases/cdrom0";
20705      } else {
20706         if($machine =~ /^sun/) {
20707            # on sparc and old suns
20708            $CD_DEVICE="/dev/rdsk/c0t6d0s0";
20709         } else {
20710            # on intel
20711            $CD_DEVICE="/dev/rdsk/c1t0d0p0";
20712         }
20713      }
20714   } elsif($os =~ /BSD/i) {  # works for netbsd, infos for other bsds welcome
20715      # /usr/include/sys/cdio.h
20716
20717      $CDROMREADTOCHDR=0x40046304;
20718      $CDROMREADTOCENTRY=0xc0086305;
20719
20720      if($BITS_64) {
20721         $CDROMREADTOCENTRY=0xc0106305;
20722      }
20723
20724      $CD_DEVICE="/dev/cd0a";
20725
20726      if($os eq "OpenBSD") {
20727         $CD_DEVICE="/dev/cd0c";
20728      }
20729   }
20730}
20731
20732sub read_toc {
20733  my $device=shift;
20734  my $tochdr=chr(0) x 16;
20735
20736  set_standard_values();
20737
20738  sysopen (CD,$device, O_RDONLY | O_NONBLOCK) or die "cannot open cdrom [$!] [$device]";
20739  #print "\n read_toc --> \$tochdr = $tochdr\n";
20740  #print "\n read_toc --> \$device = $device\n";
20741  #print "\n read_toc --> \$CDROMREADTOCHDR = $CDROMREADTOCHDR\n";
20742  ioctl(CD, $CDROMREADTOCHDR, $tochdr) or die "cannot read toc [$!] [$device]";
20743
20744  my ($start,$end);
20745  if($os =~ /BSD/) {
20746    ($start,$end)=unpack "CC",(substr $tochdr,2,2);
20747  } else {
20748    ($start,$end)=unpack "CC",$tochdr;
20749  }
20750  print STDERR "start track: $start, end track: $end\n" if $debug;
20751
20752  my @tracks=();
20753
20754  for (my $i=$start; $i<=$end;$i++) {
20755    push @tracks,$i;
20756  }
20757  push @tracks,0xAA;
20758
20759  my @r=();
20760  my $tocentry;
20761  my $toc="";
20762  my $size=0;
20763  for(@tracks) {
20764    $toc.="        ";
20765    $size+=8;
20766  }
20767
20768  if($os =~ /BSD/) {
20769    my $size_hi=int($size / 256);
20770    my $size_lo=$size & 255;
20771
20772    if($BIG_ENDIAN) {
20773      if($BITS_64) {
20774        # better but just perl >= 5.8.0
20775        # $tocentry=pack "CCCCx![P]P", $CDROM_MSF,0,$size_hi,$size_lo,$toc;
20776        $tocentry=pack "CCCCxxxxP", $CDROM_MSF,0,$size_hi,$size_lo,$toc;
20777      } else {
20778        $tocentry=pack "CCCCP8l", $CDROM_MSF,0,$size_hi,$size_lo,$toc;
20779      }
20780    } else {
20781      if($BITS_64) {
20782        $tocentry=pack "CCCCxxxxP", $CDROM_MSF,0,$size_lo,$size_hi,$toc;
20783      } else {
20784        $tocentry=pack "CCCCP8l", $CDROM_MSF,0,$size_lo,$size_hi,$toc;
20785      }
20786    }
20787    ioctl(CD, $CDROMREADTOCENTRY, $tocentry) or die "cannot read track info [$!] [$device]";
20788  }
20789
20790  my $count=0;
20791  foreach my $i (@tracks) {
20792    my ($min,$sec,$frame);
20793    unless($os =~ /BSD/) {
20794      $tocentry=pack "CCC", $i,0,$CDROM_MSF;
20795      $tocentry.=chr(0) x 16;
20796      ioctl(CD, $CDROMREADTOCENTRY, $tocentry) or die "cannot read track $i info [$!] [$device]";
20797      ($min,$sec,$frame)=unpack "CCCC", substr($tocentry,4,4);
20798    } else {
20799      ($min,$sec,$frame)=unpack "CCC", substr($toc,$count+5,3);
20800    }
20801    $count+=8;
20802
20803    my %cdtoc=();
20804
20805    $cdtoc{min}=$min;
20806    $cdtoc{sec}=$sec;
20807    $cdtoc{frame}=$frame;
20808    $cdtoc{frames}=int($frame+$sec*75+$min*60*75);
20809
20810    my $data = unpack("C",substr($tocentry,1,1));
20811    $cdtoc{data} = 0;
20812    if($data & 0x40) {
20813      $cdtoc{data} = 1;
20814    }
20815
20816    push @r,\%cdtoc;
20817  }
20818  close(CD);
20819
20820  return @r;
20821}
20822
20823sub cddb_sum {
20824  my $n=shift;
20825  my $ret=0;
20826
20827  while ($n > 0) {
20828    $ret += ($n % 10);
20829    $n = int $n / 10;
20830  }
20831  return $ret;
20832}
20833
20834sub cddb_discid {
20835  my $total=shift;
20836  my $toc=shift;
20837
20838  my $i=0;
20839  my $t=0;
20840  my $n=0;
20841
20842  while ($i < $total) {
20843    $n = $n + cddb_sum(($toc->[$i]->{min} * 60) + $toc->[$i]->{sec});
20844    $i++;
20845  }
20846  $t = (($toc->[$total]->{min} * 60) + $toc->[$total]->{sec}) -
20847      (($toc->[0]->{min} * 60) + $toc->[0]->{sec});
20848  return (($n % 0xff) << 24 | $t << 8 | $total);
20849}
20850
20851sub get_discids {
20852  my $cd=shift;
20853  $CD_DEVICE = $cd if (defined($cd));
20854
20855  my @toc=read_toc($CD_DEVICE);
20856  my $total=$#toc;
20857
20858  my $id=cddb_discid($total,\@toc);
20859
20860  return [$id,$total,\@toc];
20861}
20862
20863sub get_cddb {
20864  my $config=shift;
20865  my $diskid=shift;
20866  my $id;
20867  my $toc;
20868  my $total;
20869  my @r;
20870
20871  set_standard_values();
20872
20873  my $input = $config->{input};
20874  my $multi = $config->{multi};
20875  $input = 0 if $multi;
20876
20877  print STDERR Dumper($config) if $debug;
20878
20879  $CDDB_HOST = $config->{CDDB_HOST} if (defined($config->{CDDB_HOST}));
20880  $CDDB_PORT = $config->{CDDB_PORT} if (defined($config->{CDDB_PORT}));
20881  $CDDB_MODE = $config->{CDDB_MODE} if (defined($config->{CDDB_MODE}));
20882  $CD_DEVICE = $config->{CD_DEVICE} if (defined($config->{CD_DEVICE}));
20883  $HELLO_ID  = $config->{HELLO_ID} if (defined($config->{HELLO_ID}));
20884  $PROTO_VERSION  = $config->{PROTO_VERSION} if (defined($config->{PROTO_VERSION}));
20885  my $HTTP_PROXY = $config->{HTTP_PROXY} if (defined($config->{HTTP_PROXY}));
20886  my $FW=1 if (defined($config->{FW}));
20887
20888  if(defined($diskid)) {
20889    $id=$diskid->[0];
20890    $total=$diskid->[1];
20891    $toc=$diskid->[2];
20892  } else {
20893    my $diskid=get_discids($CD_DEVICE);
20894    $id=$diskid->[0];
20895    $total=$diskid->[1];
20896    $toc=$diskid->[2];
20897  }
20898
20899  my @list=();
20900  my $return;
20901  my $socket;
20902
20903  my $id2 = sprintf "%08x", $id;
20904  my $query = "cddb query $id2 $total";
20905  for (my $i=0; $i<$total ;$i++) {
20906      $query.=" $toc->[$i]->{frames}";
20907  }
20908
20909  # this was to old total calculation, does not work too well, its included if new version makes problems
20910  # $query.=" ". int(($toc->[$total]->{frames}-$toc->[0]->{frames})/75);
20911
20912  $query.=" ". int(($toc->[$total]->{frames})/75);
20913
20914  print Dumper($toc) if $debug;
20915
20916  if ($CDDB_MODE eq "cddb") {
20917    print STDERR "cddb: connecting to $CDDB_HOST:$CDDB_PORT\n" if $debug;
20918
20919    $socket=IO::Socket::INET->new(PeerAddr=>$CDDB_HOST, PeerPort=>$CDDB_PORT,
20920        Proto=>"tcp",Type=>SOCK_STREAM) or die "cannot connect to cddb db: $CDDB_HOST:$CDDB_PORT [$!]";
20921
20922    $return=<$socket>;
20923    unless ($return =~ /^2\d\d\s+/) {
20924      die "not welcome at cddb db";
20925    }
20926
20927    print $socket "cddb hello $HELLO_ID\n";
20928
20929    $return=<$socket>;
20930    print STDERR "hello return: $return" if $debug;
20931    unless ($return =~ /^2\d\d\s+/) {
20932      die "handshake error at cddb db: $CDDB_HOST:$CDDB_PORT";
20933    }
20934
20935    print $socket "proto $PROTO_VERSION\n";
20936
20937    $return=<$socket>;
20938    print STDERR "proto return: $return" if $debug;
20939    unless ($return =~ /^2\d\d\s+/) {
20940      die "protokoll mismatch error at cddb db: $CDDB_HOST:$CDDB_PORT";
20941    }
20942
20943    print STDERR "cddb: sending: $query\n" if $debug;
20944    print $socket "$query\n";
20945
20946    $return=<$socket>;
20947    chomp $return;
20948
20949    print STDERR "cddb: result: $return\n" if $debug;
20950  } elsif ($CDDB_MODE eq "http") {
20951    my $query2=$query;
20952    $query2 =~ s/ /+/g;
20953    my $id=$HELLO_ID;
20954    $id =~ s/ /+/g;
20955
20956    my $url = "/~cddb/cddb.cgi?cmd=$query2&hello=$id&proto=$PROTO_VERSION";
20957
20958    my $host=$CDDB_HOST;
20959    my $port=80;
20960
20961    my ($user,$pass);
20962
20963    if($HTTP_PROXY) {
20964      if($HTTP_PROXY =~ /^(http:\/\/|)(.+?):(.+)\@(.+?):(.+)/) {
20965        $user=$2;
20966        $pass=$3;
20967        $host=$4;
20968        $port=$5;
20969      } elsif($HTTP_PROXY =~ /^(http:\/\/|)(.+?):(\d+)/) {
20970        $host=$2;
20971        $port=$3;
20972      }
20973      $url="http://$CDDB_HOST".$url." HTTP/1.0";
20974    }
20975
20976    print STDERR "cddb: connecting to $host:$port\n" if $debug;
20977
20978    $socket=IO::Socket::INET->new(PeerAddr=>$host, PeerPort=>$port,
20979        Proto=>"tcp",Type=>SOCK_STREAM) or die "cannot connect to cddb db: $host:$port [$!]";
20980
20981    print STDERR "cddb: http send: GET $url\n" if $debug;
20982    print $socket "GET $url\n";
20983
20984    if($user) {
20985      my $cred = encode_base64("$user:$pass");
20986      print $socket "Proxy-Authorization: Basic $cred\n";
20987    }
20988
20989    print $socket "\n";
20990    print $socket "\n" if $FW;
20991
20992    if($HTTP_PROXY) {
20993      while(<$socket> =~ /^\S+/){};
20994    }
20995
20996    $return=<$socket>;
20997    chomp $return;
20998
20999    print STDERR "cddb: http result: $return\n" if $debug;
21000  } else {
21001    die "unkown mode: $CDDB_MODE for querying cddb";
21002  }
21003
21004  $return =~ s/\r//g;
21005
21006  my ($err) = $return =~ /^(\d\d\d)\s+/;
21007  unless ($err =~ /^2/) {
21008    die "query error at cddb db: $CDDB_HOST:$CDDB_PORT";
21009  }
21010
21011  if($err==202) {
21012    return undef;
21013  } elsif(($err==211) || ($err==210)) {
21014    while(<$socket>) {
21015      last if(/^\./);
21016      push @list,$_;
21017      s/\r//g;
21018      print STDERR "unexact: $_" if $debug;
21019    }
21020  } elsif($err==200) {
21021    $return =~ s/^200 //;
21022    push @list,$return;
21023  } else {
21024    die "cddb: unknown: $return";
21025  }
21026
21027  my @to_get;
21028
21029  unless($multi) {
21030    if (@list) {
21031      my $index;
21032      if($input==1) {
21033        print "This CD could be:\n\n";
21034        my $i=1;
21035        for(@list) {
21036          my ($tit) = $_ =~ /^\S+\s+\S+\s+(.*)/;
21037          print "$i: $tit\n";
21038          $i++
21039        }
21040        print "\n0: none of the above\n\nChoose: ";
21041        my $n=<STDIN>;
21042        $index=int($n);
21043      } else {
21044        $index=1;
21045      }
21046
21047      if ($index == 0) {
21048        return undef;
21049      } else {
21050        push @to_get,$list[$index-1];
21051      }
21052    }
21053  } else {
21054    push @to_get,@list;
21055  }
21056
21057  my $i=0;
21058  for my $get (@to_get) {
21059    #200 misc 0a01e802 Meredith Brooks / Bitch Single
21060    my ($cat,$id,$at) = $get =~ /^(\S+?)\s+(\S+?)\s+(.*)/;
21061
21062    my $artist;
21063    my $title;
21064
21065    if($at =~ /\//) {
21066      ($artist,$title)= $at =~ /^(.*?)\s\/\s(.*)/;
21067    } else {
21068      $artist=$at;
21069      $title=$at;
21070    }
21071
21072    my %cd=();
21073    $cd{artist}=$artist;
21074    chomp $title;
21075    $title =~ s/\r//g;
21076    $cd{title}=$title;
21077    $cd{cat}=$cat;
21078    $cd{id}=$id;
21079
21080    my @lines;
21081
21082    $query="cddb read $cat $id";
21083
21084    if ($CDDB_MODE eq "cddb") {
21085      print STDERR "cddb: getting: $query\n" if $debug;
21086      print $socket "$query\n";
21087
21088      while(<$socket>) {
21089        last if(/^\./);
21090        push @lines,$_;
21091      }
21092      if(@to_get-1 == $i) {
21093        print $socket "quit\n";
21094        close $socket;
21095      }
21096
21097    } elsif ($CDDB_MODE eq "http") {
21098      close $socket;
21099
21100      my $query2=$query;
21101      $query2 =~ s/ /+/g;
21102      my $id=$HELLO_ID;
21103      $id =~ s/ /+/g;
21104
21105      my $url = "/~cddb/cddb.cgi?cmd=$query2&hello=$id&proto=$PROTO_VERSION";
21106
21107      my $host=$CDDB_HOST;
21108      my $port=80;
21109
21110      my ($user,$pass);
21111
21112      if($HTTP_PROXY) {
21113        if($HTTP_PROXY =~ /^(http:\/\/|)(.+?):(.+)\@(.+?):(.+)/) {
21114          $user=$2;
21115          $pass=$3;
21116          $host=$4;
21117          $port=$5;
21118        } elsif($HTTP_PROXY =~ /^(http:\/\/|)(.+?):(\d+)/) {
21119          $host=$2;
21120          $port=$3;
21121        }
21122        $url="http://$CDDB_HOST".$url." HTTP/1.0";
21123      }
21124
21125      print STDERR "cddb: connecting to $host:$port\n" if $debug;
21126
21127      $socket=IO::Socket::INET->new(PeerAddr=>$host, PeerPort=>$port,
21128        Proto=>"tcp",Type=>SOCK_STREAM) or die "cannot connect to cddb db: $host:$port [$!]";
21129
21130      print STDERR "cddb: http send: GET $url\n" if $debug;
21131      print $socket "GET $url\n";
21132
21133      if($user) {
21134        my $cred = encode_base64("$user:$pass");
21135        print $socket "Proxy-Authorization: Basic $cred\n";
21136      }
21137
21138      print $socket "\n";
21139      print $socket "\n" if $FW;
21140
21141      if($HTTP_PROXY) {
21142        while(<$socket> =~ /^\S+/){};
21143      }
21144
21145      while(<$socket>) {
21146        last if(/^\./);
21147        push @lines,$_;
21148      }
21149      close $socket;
21150    } else {
21151      die "unkown mode: $CDDB_MODE for querying cddb";
21152    }
21153
21154    # xmcd
21155    #
21156    # Track frame offsets:
21157    #	150
21158    # ...
21159    #	210627
21160    #
21161    # Disc length: 2952 seconds
21162    #
21163    # Revision: 1
21164    # Submitted via: xmcd 2.0
21165    #
21166
21167    for(@lines) {
21168      last if(/^\./);
21169      next if(/^\d\d\d/);
21170      push @{$cd{raw}},$_;
21171      #TTITLE0=Bitch (Edit)
21172      if(/^TTITLE(\d+)\=\s*(.*)/) {
21173        my $t= $2;
21174        chop $t;
21175        $cd{frames}[$1]=$toc->[$1]->{frames};
21176        $cd{data}[$1]=$toc->[$1]->{data};
21177        unless (defined $cd{track}[$1]) {
21178          $cd{track}[$1]=$t;
21179        } else {
21180          $cd{track}[$1]=$cd{track}[$1].$t;
21181        }
21182      } elsif(/^DYEAR=\s*(\d+)/) {
21183        $cd{'year'} = $1;
21184      } elsif(/^DGENRE=\s*(\S+.*)/) {
21185        my $t = $1;
21186        chop $t;
21187        $cd{'genre'} = $t;
21188      } elsif(/^\#\s+Revision:\s+(\d+)/) {
21189        $cd{'revision'} = $1;
21190      }
21191    }
21192
21193    $cd{tno}=$#{$cd{track}}+1;
21194    $cd{frames}[$cd{tno}]=$toc->[$cd{tno}]->{frames};
21195
21196    return %cd unless($multi);
21197    push @r,\%cd;
21198    $i++;
21199  }
21200
21201  return @r;
21202}
21203
21204
21205   1;  # don't forget to return a true value from the file
21206} # end scope block
21207
21208