File Coverage

blib/lib/Audio/GtkGramofile/Logic.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Audio::GtkGramofile::Logic;
2              
3 1     1   2110 use strict;
  1         3  
  1         35  
4 1     1   5 use warnings;
  1         2  
  1         29  
5 1     1   8 use Carp;
  1         2  
  1         126  
6              
7 1     1   12531 use Glib 1.040, qw(TRUE FALSE);
  0            
  0            
8             use IO::File;
9             use Tie::Scalar;
10             use POSIX; #needed for floor()
11             use Audio::Gramofile;
12              
13             use vars qw($VERSION);
14             $VERSION = do { my @r = (q$Revision: 1.5 $ =~ /\d+/g); shift @r; sprintf("0.%04d",@r) }; # must be all one line, for MakeMaker
15              
16             use constant WINDOW_WIDTH => 450;
17             use constant PBAR_HEIGHT => 30;
18             use constant PBAR_OFFSET => 60;
19              
20             sub new {
21             my $proto = shift;
22             my $args = shift;
23             my $class = ref($proto) || $proto;
24            
25             my $self = {};
26             bless $self, $class;
27            
28             return $self;
29             }
30              
31             sub set_gtkgramofile {
32             my $self = shift;
33             my $gtkgramofile = shift;
34              
35             $self->{gtkgramofile} = $gtkgramofile;
36             }
37              
38             sub tracksplit_watch_callback {
39             my ($fd, $condition, $data) = @_;
40              
41             my $fh = $data->{fh};
42             my $filelist = $data->{params}->[2];
43             my $index = $data->{params}->[3] - 1;
44             my $file = $filelist->[$index];
45              
46             if ($condition >= 'in') {
47             # there's data available for reading. we have no
48             # guarantee that all the data is there, just that
49             # some is there. however, we know that the child
50             # will be writing full lines, so we'll assume that
51             # we have lines and will just use <>.
52             my $line = scalar <$fh>;
53             if (defined $line) {
54             # do something useful with the text.
55             if ($line =~ $data->{done_regexp}) {
56             my $fraction = $1;
57             $fraction /= 100.0;
58             $data->{progress}->set_fraction($fraction);
59             } elsif ($line =~ $data->{end_regexp}) {
60             my $tracks = $1;
61             $data->{progress}->set_fraction(1.0);
62             $data->{progress}->set_text($tracks . " tracks found in " . $file);
63             }
64             }
65             }
66              
67             if ($condition >= 'hup' or $condition >= 'err') { # End Of File, Hang UP, or ERRor.
68             $fh->close;
69             $fh = undef;
70             }
71              
72             if (defined $fh) { # the file handle is still open, so return TRUE to stay installed and be called again.
73             return TRUE;
74             } else {
75             &{$data->{sub}}($data->{params});
76             return FALSE;
77             }
78             }
79              
80             sub tracksplit {
81             my $self = shift;
82             my $filelist = shift;
83             my $make_use_rms = shift;
84             my $make_graphs = shift;
85             my $blocklen = shift;
86             my $global_silence_factor = shift;
87             my $local_silence_threshold = shift;
88             my $min_silence_blocks = shift;
89             my $min_track_blocks = shift;
90             my $extra_blocks_start = shift;
91             my $extra_blocks_end = shift;
92              
93             $self->{gtkgramofile}->{gui}->{start_tracksplit_button}->set_sensitive(0);
94             my $window = Gtk2::Window->new;
95             $window->set_title('gramofile tracksplit');
96             $window->signal_connect (delete_event => sub {$self->{gtkgramofile}->{gui}->{start_tracksplit_button}->set_sensitive(TRUE); $window->destroy;});
97             $window->set_default_size (WINDOW_WIDTH, @$filelist * PBAR_HEIGHT + PBAR_OFFSET);
98             my $vbox = Gtk2::VBox->new;
99             $window->add ($vbox);
100             $self->{gtkgramofile}->set_value('tracksplit_general','tracksplit_cancel_button', Gtk2::Button->new_from_stock('ar_cancel'));
101             $self->{gtkgramofile}->get_value('tracksplit_general','tracksplit_cancel_button')->signal_connect (clicked => sub {$self->{gtkgramofile}->{gui}->{start_tracksplit_button}->set_sensitive(TRUE); $window->destroy;});
102             $self->{gtkgramofile}->get_value('tracksplit_general','tracksplit_cancel_button')->set_sensitive(FALSE);
103             $vbox->pack_end($self->{gtkgramofile}->get_value('tracksplit_general','tracksplit_cancel_button'), FALSE, FALSE, 0);
104             $window->show_all;
105            
106             my $gramofile = Audio::Gramofile->new or croak "Can't make a new Gramofile object, $!";
107             $gramofile->init_tracksplit("make_use_rms" => $make_use_rms) if ($make_use_rms);
108             $gramofile->init_tracksplit("make_graphs" => $make_graphs) if ($make_graphs);
109             $gramofile->init_tracksplit("blocklen" => $blocklen) if ($blocklen);
110             $gramofile->init_tracksplit("global_silence_factor" => $global_silence_factor) if ($global_silence_factor);
111             $gramofile->init_tracksplit("local_silence_threshold" => $local_silence_threshold) if ($local_silence_threshold);
112             $gramofile->init_tracksplit("min_silence_blocks" => $min_silence_blocks) if ($min_silence_blocks);
113             $gramofile->init_tracksplit("min_track_blocks" => $min_track_blocks) if ($min_track_blocks);
114             $gramofile->init_tracksplit("extra_blocks_start" => $extra_blocks_start) if ($extra_blocks_start);
115             $gramofile->init_tracksplit("extra_blocks_end" => $extra_blocks_end) if ($extra_blocks_end);
116              
117             my $index = 0;
118             tracksplit_one([$self, $gramofile, $filelist, $index, $vbox]);
119             }
120              
121             sub tracksplit_one {
122             my $params = shift;
123             my $self = $params->[0];
124             my $gramofile = $params->[1];
125             my $filelist = $params->[2];
126             my $index = $params->[3];
127             my $vbox = $params->[4];
128            
129             my $pid_file = $self->{gtkgramofile}->get_value('tracksplit_general','tracksplit_pid_file');
130              
131             if ($index == @$filelist or $self->{gtkgramofile}->get_value('tracksplit_general','tracksplit_stopped')) { # the worklist is empty
132             my $label;
133             if ($self->{gtkgramofile}->get_value('tracksplit_general','tracksplit_stopped')) {
134             $label = Gtk2::Label->new("Track splitting aborted!");
135             } else {
136             my $text = "Track location complete! More information is in the '.tracks' file";
137             $text .= ($index - 1) ? "s." : ".";
138             $label = Gtk2::Label->new($text);
139             }
140             $vbox->pack_start($label, FALSE, FALSE, 0);
141             $vbox->show_all;
142             $self->{gtkgramofile}->get_value('tracksplit_general','tracksplit_cancel_button')->set_sensitive(TRUE);
143             $self->{gtkgramofile}->{gui}->{start_tracksplit_button}->set_sensitive(TRUE);
144             unlink $pid_file or croak "Can't unlink $pid_file";
145             return;
146             }
147              
148             my $pbar = Gtk2::ProgressBar->new;
149             $pbar->set_pulse_step(0.05);
150             $pbar->set_text('Locating tracks in ' . $filelist->[$index]);
151             $vbox->pack_start($pbar, FALSE, FALSE, 0);
152             $vbox->show_all;
153              
154             my $file = $filelist->[$index];
155             my $fh = IO::File->new; # we use IO::File to get a unique file handle.
156             my $pid = $fh->open ("-|"); # fork a copy of ourselves, and read the child's stdout.
157             croak "can't fork: $!\n" unless defined $pid;
158             if ($pid == 0) { # in child
159             eval{
160             $gramofile->set_input_file($file);
161             $gramofile->split_to_tracks;
162             };
163             carp $@ if $@;
164             exit; # important! do not continue to run or Very Bad Things can and will happen!
165             } else { # in parent
166             my $pid_fh = IO::File->new(">$pid_file") or croak "Can't open $pid_file, $!";
167             print $pid_fh $pid;
168             $pid_fh->close;
169             $index++;
170             my $data = {fh => $fh, progress => $pbar, done_regexp => qr/^Done :\s+(\d+) %$/,
171             end_regexp => qr/^(\d+) tracks have been detected/,
172             sub => \&tracksplit_one, params => [$self, $gramofile, $filelist, $index, $vbox]};
173             Glib::IO->add_watch ($fh->fileno, [qw/in hup err/], \&tracksplit_watch_callback, $data);
174             }
175             }
176              
177             my ($track_num, $total_num);
178             sub process_watch_callback {
179             my ($fd, $condition, $data) = @_;
180              
181             my $fh = $data->{fh};
182             my $filelist = $data->{params}->[2];
183             my $index = $data->{params}->[4] - 1;
184             my $file = $filelist->[$index];
185              
186             if ($condition >= 'in') { # there's data available for reading.
187             my $line = scalar <$fh>;
188             if (defined $line) {
189             if ($line =~ $data->{track_regexp}) {
190             ($track_num, $total_num) = ($1, $2);
191             $data->{progress}->set_text("Processing track $track_num of $total_num in $file");
192             } elsif ($line =~ $data->{done_regexp}) {
193             my ($track_f, $total_f) = ($1, $2);
194             $total_f /= 100.0;
195             $data->{progress}->set_fraction($total_f);
196             $data->{progress}->set_text("Processing track " . $track_num . " of " . $total_num . " in $file - ${track_f}%");
197             }
198             }
199             }
200              
201             if ($condition >= 'hup' or $condition >= 'err') { # End Of File, Hang UP, or ERRor.
202             $data->{progress}->set_text("Processed " . $total_num . " tracks in $file");
203             $data->{progress}->set_fraction(1.0);
204             $fh->close;
205             $fh = undef;
206             }
207              
208             if (defined $fh) { # the file handle is still open, so return TRUE to stay installed and be called again.
209             return TRUE;
210             } else {
211             &{$data->{sub}}($data->{params});
212             return FALSE;
213             }
214             }
215              
216             sub process_signal {
217             my $self = shift;
218             my $input_file_list_ref = shift;
219             my $output_file_list_ref = shift;
220             my $filter_list_ref = shift;
221             my $simple_median_num_samples = shift;
222             my $double_median_first_num_samples = shift;
223             my $double_median_second_num_samples = shift;
224             my $simple_mean_num_samples = shift;
225             my $rms_filter_num_samples = shift;
226             my $cmf_median_tick_num_samples = shift;
227             my $cmf_rms_length = shift;
228             my $cmf_recursive_median_length = shift;
229             my $cmf_decimation_factor = shift;
230             my $cmf_tick_detection_threshold = shift;
231             my $cmf2_rms_length = shift;
232             my $cmf2_recursive_median_length = shift;
233             my $cmf2_decimation_factor = shift;
234             my $cmf2_tick_fine_threshold = shift;
235             my $cmf2_tick_detection_threshold = shift;
236             my $cmf3_rms_length = shift;
237             my $cmf3_recursive_median_length = shift;
238             my $cmf3_decimation_factor = shift;
239             my $cmf3_tick_fine_threshold = shift;
240             my $cmf3_tick_detection_threshold = shift;
241             my $cmf3_fft_length = shift;
242             my $simple_normalize_factor = shift;
243             my $begin_and_end_times = shift;
244             my $begin_time = shift;
245             my $end_time = shift;
246             my $whole_frames = shift;
247             my $framesize = shift;
248              
249             my $window = Gtk2::Window->new;
250             $window->set_title('gramofile signal processing');
251             $self->{gtkgramofile}->{gui}->{start_process_button}->set_sensitive(0);
252             $self->{gtkgramofile}->set_value('process_general','process_textview',Gtk2::TextView->new);
253             $self->{gtkgramofile}->set_value('process_general','process_cancel_button',Gtk2::Button->new_from_stock('ar_cancel'));
254             $self->{gtkgramofile}->get_value('process_general','process_cancel_button')->signal_connect (clicked => sub {$self->{gtkgramofile}->{gui}->{start_process_button}->set_sensitive(TRUE); $window->destroy;});
255             $self->{gtkgramofile}->get_value('process_general','process_cancel_button')->set_sensitive(FALSE);
256             $window->signal_connect (delete_event => sub {$self->{gtkgramofile}->{gui}->{start_process_button}->set_sensitive(TRUE); $window->destroy;});
257             $window->set_default_size (WINDOW_WIDTH, @$input_file_list_ref * PBAR_HEIGHT + PBAR_OFFSET);
258             my $vbox = Gtk2::VBox->new;
259             $window->add($vbox);
260             $self->{gtkgramofile}->set_value('process_general','process_cancel_button', Gtk2::Button->new_from_stock('ar_cancel'));
261             $self->{gtkgramofile}->get_value('process_general','process_cancel_button')->signal_connect (clicked => sub {$self->{gtkgramofile}->{gui}->{start_process_button}->set_sensitive(1); $window->destroy;});
262             $self->{gtkgramofile}->get_value('process_general','process_cancel_button')->set_sensitive(FALSE);
263             $vbox->pack_end($self->{gtkgramofile}->get_value('process_general','process_cancel_button'), FALSE, FALSE, 0);
264             $window->show_all;
265              
266             my $gramofile = Audio::Gramofile->new or croak "Can't make a new Gramofile object, $!";
267            
268             $gramofile->init_filter_tracks(@$filter_list_ref);
269              
270             $gramofile->init_simple_median_filter("num_samples" => $simple_median_num_samples)
271             if ($simple_median_num_samples);
272            
273             $gramofile->init_double_median_filter("first_num_samples" => $double_median_first_num_samples)
274             if ($double_median_first_num_samples);
275             $gramofile->init_double_median_filter("second_num_samples" => $double_median_second_num_samples)
276             if ($double_median_second_num_samples);
277            
278             $gramofile->init_simple_mean_filter("num_samples" => $simple_mean_num_samples)
279             if ($simple_mean_num_samples);
280            
281             $gramofile->init_rms_filter("num_samples" => $rms_filter_num_samples)
282             if ($rms_filter_num_samples);
283            
284             $gramofile->init_cmf_filter("num_samples" => $cmf_median_tick_num_samples)
285             if ($cmf_median_tick_num_samples);
286             $gramofile->init_cmf_filter("rms_length" => $cmf_rms_length)
287             if ($cmf_rms_length);
288             $gramofile->init_cmf_filter("rec_med_len" => $cmf_recursive_median_length)
289             if ($cmf_recursive_median_length);
290             $gramofile->init_cmf_filter("rec_med_dec" => $cmf_decimation_factor)
291             if ($cmf_decimation_factor);
292             $gramofile->init_cmf_filter("tick_threshold" => $cmf_tick_detection_threshold)
293             if ($cmf_tick_detection_threshold);
294            
295             $gramofile->init_cmf2_filter("rms_length" => $cmf2_rms_length)
296             if ($cmf2_rms_length);
297             $gramofile->init_cmf2_filter("rec_med_len" => $cmf2_recursive_median_length)
298             if ($cmf2_recursive_median_length);
299             $gramofile->init_cmf2_filter("rec_med_dec" => $cmf2_decimation_factor)
300             if ($cmf2_decimation_factor);
301             $gramofile->init_cmf2_filter("fine_threshold" => $cmf2_tick_fine_threshold)
302             if ($cmf2_tick_fine_threshold);
303             $gramofile->init_cmf2_filter("tick_threshold" => $cmf2_tick_detection_threshold)
304             if ($cmf2_tick_detection_threshold);
305            
306             $gramofile->init_cmf3_filter("rms_length" => $cmf3_rms_length)
307             if ($cmf3_rms_length);
308             $gramofile->init_cmf3_filter("rec_med_len" => $cmf3_recursive_median_length)
309             if ($cmf3_recursive_median_length);
310             $gramofile->init_cmf3_filter("rec_med_dec" => $cmf3_decimation_factor)
311             if ($cmf3_decimation_factor);
312             $gramofile->init_cmf3_filter("fine_threshold" => $cmf3_tick_fine_threshold)
313             if ($cmf3_tick_fine_threshold);
314             $gramofile->init_cmf3_filter("tick_threshold" => $cmf3_tick_detection_threshold)
315             if ($cmf3_tick_detection_threshold);
316             $gramofile->init_cmf3_filter("fft_length" => $cmf3_fft_length)
317             if ($cmf3_fft_length);
318            
319             $gramofile->init_simple_normalize_filter("normalize_factor" => $simple_normalize_factor)
320             if ($simple_normalize_factor);
321              
322             $gramofile->use_begin_end_time($begin_time, $end_time) if ($begin_and_end_times);
323              
324             $gramofile->adjust_frames($framesize) if ($whole_frames);
325              
326             my $index = 0;
327             process_one([$self, $gramofile, $input_file_list_ref, $output_file_list_ref, $index, $vbox]);
328             }
329              
330             sub process_one {
331             my $params = shift;
332             my $self = $params->[0];
333             my $gramofile = $params->[1];
334             my $infilelist = $params->[2];
335             my $outfilelist = $params->[3];
336             my $index = $params->[4];
337             my $vbox = $params->[5];
338            
339             my $pid_file = $self->{gtkgramofile}->get_value('process_general','process_pid_file');
340             if ($index == @$infilelist or $self->{gtkgramofile}->get_value('process_general','process_stopped')) {
341             my $label;
342             if ($self->{gtkgramofile}->get_value('process_general','process_stopped')) {
343             $label = Gtk2::Label->new("Signal processing aborted!");
344             } else {
345             my $text = ($index - 1) ? "All .wav files " : ".wav file ";
346             $text .= "split and processed!";
347             $label = Gtk2::Label->new($text);
348             }
349             $vbox->pack_start($label, FALSE, FALSE, 0);
350             $vbox->show_all;
351             $self->{gtkgramofile}->get_value('process_general','process_cancel_button')->set_sensitive(TRUE);
352             $self->{gtkgramofile}->{gui}->{start_process_button}->set_sensitive(0);
353             unlink $pid_file or croak "Can't unlink $pid_file";
354             return;
355             }
356              
357             my $pbar = Gtk2::ProgressBar->new;
358             $pbar->set_pulse_step(0.05);
359             $pbar->set_text('Processing ' . $infilelist->[$index]);
360             $vbox->pack_start($pbar, FALSE, FALSE, 0);
361             $vbox->show_all;
362              
363             my $input_file = $infilelist->[$index];
364             my $output_file = $outfilelist->[$index];
365             my $fh = IO::File->new; # we use IO::File to get a unique file handle.
366             my $pid = $fh->open ("-|"); # fork a copy of ourselves, and read the child's stdout.
367             croak "can't fork: $!\n" unless defined $pid;
368             if ($pid == 0) { # in child.
369             eval {
370             $gramofile->set_input_file($input_file);
371             $gramofile->set_output_file($output_file);
372             $gramofile->filter_tracks;
373             };
374             carp $@ if $@;
375             exit; # important! do not continue to run or Very Bad Things can and will happen!
376             } else { # in parent.
377             my $pid_fh = IO::File->new(">$pid_file") or croak "Can't open $pid_file, $!";
378             print $pid_fh $pid;
379             $pid_fh->close;
380             $index++;
381             my $data = {fh => $fh, progress => $pbar, track_regexp => qr/^Track:\s+(\d+)\s+of\s+(\d+)\.\s*$/,
382             done_regexp => qr/^Done:\s+(\d+)%\s+track\s+(\d+)%\s+total\s*$/,
383             sub => \&process_one, params => [$self, $gramofile, $infilelist, $outfilelist, $index, $vbox]};
384             Glib::IO->add_watch ($fh->fileno, [qw/in hup err/], \&process_watch_callback, $data);
385             }
386             }
387              
388             1;