/usr/local/CPAN/Audio-GtkGramofile/Audio/GtkGramofile/Signals.pm


package Audio::GtkGramofile::Signals;

use strict;
use warnings;
use Carp;

use File::Basename;
use DirHandle;
use IO::File;
use Gtk2;
use Glib 1.040, qw(TRUE FALSE);

use Audio::GtkGramofile::Settings;
use Audio::GtkGramofile::GUI;

use vars qw($VERSION);
$VERSION = do { my @r = (q$Revision: 1.5 $ =~ /\d+/g); shift @r; sprintf("0.%04d",@r) }; # must be all one line, for MakeMaker

sub new {
  my $proto = shift;
  my $class = ref($proto) || $proto;

  my $self = {};
  bless $self, $class;

  return $self;
}

sub set_gtkgramofile {
  my $self = shift;
  my $gtkgramofile = shift;
  
  $self->{gtkgramofile} = $gtkgramofile;
}

sub set_times_dialog {
  my $self = shift;

  $self->{times_dialog} = $self->times_dialog;
  $self->{times_dialog};
}

sub set_frame_dialog {
  my $self = shift;

  $self->{frame_dialog} = $self->frame_dialog;
  $self->{frame_dialog};
}

sub get_callback {
  my $self = shift;
  my $string = shift;

  return \&{"on_".$string."_clicked"};
}

sub on_quit_clicked {Gtk2->main_quit;}
sub quit_gramofile {shift->on_quit_clicked}

sub on_record_clicked {
    system("gnome-sound-recorder") == 0 or croak "system gnome-sound-recorder failed: $?";
}

sub on_play_clicked {
    system("gnome-sound-recorder") == 0 or croak "system gnome-sound-recorder failed: $?";
}

sub on_generic_browse_clicked {
  my $self = shift;
  my $setting = shift;
  my $button = shift;
  my $windowname = shift; 
  my $labeltext = shift; 
  my $filename = shift; 
  my $filename_entry = shift; 

  $self->{gtkgramofile}->{gui}->{$button}->set_sensitive(0);
  my $window = Gtk2::FileSelection->new($windowname);
  my $label = Gtk2::Label->new($labeltext);
  $label->show;
  $window->vbox->add($label);
  $window->vbox->set_border_width(30);
  
  $window->signal_connect (delete_event => sub {1}); # inhibit destruction by pretending to handle delete-event
  $window->signal_connect (response => sub { # handle the response, and hide the window, never destroy it
    my ($me, $response) = @_;
    $self->{gtkgramofile}->set_value($setting,$filename, ($response eq "ok") ? $window->get_filename : "");
    $self->{gtkgramofile}->{gui}->{$filename_entry}->set_text($self->{gtkgramofile}->get_value($setting,$filename));
    $self->{gtkgramofile}->{gui}->{$filename_entry}->show;
    $self->{gtkgramofile}->{gui}->{$button}->set_sensitive(1);
    $me->hide;
    1;
  });
  $window->show;
}

sub on_tracksplit_browse_clicked {
  my $widget = shift;
  my $self = shift;

  $self->on_generic_browse_clicked('tracksplit_general', 'tracksplit_browse_button', 'Record Audio',
    'Locate Tracks : Choose a file or directory', 'tracksplit_filename', 'tracksplit_filename_entry');
}

sub on_start_tracksplit_clicked {
  my $widget = shift;
  my $self = shift;
  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};

  $self->{gtkgramofile}->set_value('tracksplit_general','tracksplit_stopped', 0);
  $self->{gtkgramofile}->{gui}->{stop_tracksplit_button}->set_sensitive(1);

  my $filelist = [];
  my $filedir = $self->{gtkgramofile}->get_value('tracksplit_general','tracksplit_filename');
  if (-f $filedir) {
    push @$filelist, $filedir;
  } elsif (-d $filedir) {
    my $filter = $self->{gtkgramofile}->get_value('tracksplit_params','tracksplit_filename_filter');
    my $d = DirHandle->new($filedir);
    if (defined $d) {
       while (defined($_ = $d->read)) { 
         next if (/^\.\.?$/);
         next if ($filter and not /$filter/);
         push @$filelist, join ("/",$filedir,$_);
       }
       undef $d;
    }
  } else {
    my $message = $filedir  . ' does not exist';
    my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                    'destroy-with-parent',
                                    'gtk-ok' => 'none');
    my $label = Gtk2::Label->new ($message);
    $dialog->vbox->add ($label);

    # Ensure that the dialog box is destroyed when the user responds.
    $dialog->signal_connect (response => sub { $_[0]->destroy });
    $dialog->show_all;
    $self->{gtkgramofile}->{gui}->{stop_tracksplit_button}->set_sensitive(0);
    return;
  }
  $self->{gtkgramofile}->{logic}->tracksplit($filelist, 
             $self->{gtkgramofile}->get_value('tracksplit_params','tracksplit_rms_file'),
             $self->{gtkgramofile}->get_value('tracksplit_params','tracksplit_generate_graph'),
             $self->{gtkgramofile}->get_value('tracksplit_params','signal_power_data_blocklen'),
             $self->{gtkgramofile}->get_value('tracksplit_params','global_silence_factor'),
             $self->{gtkgramofile}->get_value('tracksplit_params','local_silence_factor'),
             $self->{gtkgramofile}->get_value('tracksplit_params','inter_track_silence_minlen'),
             $self->{gtkgramofile}->get_value('tracksplit_params','track_minlen'),
             $self->{gtkgramofile}->get_value('tracksplit_params','track_start_extra_blocks'),
             $self->{gtkgramofile}->get_value('tracksplit_params','track_end_extra_blocks'));
}

sub on_stop_generic_clicked {
  my $self = shift;
  my $section = shift;
  my $pid_f = shift;
  my $stopped = shift;
  my $cancel = shift;
  
  my $pid_file = $self->{gtkgramofile}->get_value($section, $pid_f);
  my $pid_fh = IO::File->new("<$pid_file") or croak "Can't read $pid_file, $!";
  my $pid = <$pid_fh>;
  kill 9, $pid or croak "Can't kill process id, $pid, $!";
  $pid_fh->close;
  $self->{gtkgramofile}->set_value($section, $stopped, 1);
  $self->{gtkgramofile}->get_value($section, $cancel)->set_sensitive(TRUE);
}

sub on_stop_tracksplit_clicked {
  my $widget = shift;
  my $self = shift;
  
  $self->on_stop_generic_clicked('tracksplit_general', 'tracksplit_pid_file', 
    'tracksplit_stopped', 'tracksplit_cancel_button');
}

sub on_process_infile_clicked {
  my $widget = shift;
  my $self = shift;

  $self->on_generic_browse_clicked('process_general', 'process_infile_button', 'Process Audio',
    'Process Audio : Choose an input file or directory', 'process_infile', 'process_infile_entry');
}

sub on_process_outfile_clicked {
  my $widget = shift;
  my $self = shift;

  $self->on_generic_browse_clicked('process_general', 'process_outfile_button', 'Process Audio',
    'Process Audio : Choose an output file or directory', 'process_outfile', 'process_outfile_entry');
}

sub label_and_spin {
  my $self = shift;
  my $dialog = shift;
  my $label_text = shift;
  my $parameter = shift;
  my $first = shift;
  my $last = shift;
  my $step = shift;
  my $backwards = @_ ? shift : undef;

  my $hbox = Gtk2::HBox->new;
  my $label = Gtk2::Label->new($label_text);
  my $spin = Gtk2::SpinButton->new_with_range($first, $last, $step);
  $spin->set_value($backwards) if (defined $backwards);
  $hbox->pack_start_defaults($label);
  $hbox->pack_end_defaults($spin);
  $dialog->vbox->add($hbox);
  return $spin;
}

sub on_generic_1par_filter_clicked {
  my $self = shift;
  my $button = shift;
  my $message = shift;
  my $label_text = shift;
  my $parameter = shift;
  my $first = shift;
  my $last = shift;
  my $step = shift;

  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};
  $self->{gtkgramofile}->{gui}->{$button}->set_sensitive(0);

  my $dialog = Gtk2::Dialog->new( $message, $parent,
                                  'destroy-with-parent',
                                  'gtk-ok' => 'none',
                                  'gtk-cancel' => 'cancel');
  $dialog->vbox->add(Gtk2::Label->new($message));
  $dialog->vbox->add(Gtk2::HSeparator->new);
  my $spin = $self->label_and_spin($dialog, $label_text, $parameter, $first, $last, $step);

  $dialog->signal_connect (response => sub { 
    my $window = shift;
    $self->{gtkgramofile}->set_value('process_general',$window,0);
    $self->{gtkgramofile}->set_value('process_params',$parameter,$spin->get_value_as_int);
    $self->{gtkgramofile}->{gui}->{$button}->set_sensitive(1);
    $window->destroy;
  });
  $dialog->show_all;
}

sub on_simple_median_filter_clicked {
  my $widget = shift;
  my $self = shift;

  $self->on_generic_1par_filter_clicked('simple_median_filter_button', "Simple Median Filter Properties",
    "Number of samples to take the median of", 'simple_median_num_samples', 1, 101, 2);
}

sub on_double_median_filter_clicked {
  my $widget = shift;
  my $self = shift;

  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};
  $self->{gtkgramofile}->{gui}->{double_median_filter_button}->set_sensitive(0);

  my $message = "Double Median Filter Properties";
  my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                  'destroy-with-parent',
                                  'gtk-ok' => 'none',
                                  'gtk-cancel' => 'cancel');
  $dialog->vbox->add(Gtk2::Label->new($message));
  $dialog->vbox->add(Gtk2::HSeparator->new);
  my $spin1 = $self->label_and_spin($dialog, "Number of samples for the first median", 'double_median_first_num_samples', 1, 101, 2);
  my $spin2 = $self->label_and_spin($dialog, "Number of samples for the second median", 'double_median_second_num_samples', 1, 101, 2);
  # Ensure that the dialog box is destroyed when the user responds.
  $dialog->signal_connect (response => sub { 
    my $window = shift;
    $self->{gtkgramofile}->get_value('process_params','double_median_first_num_samples', $spin1->get_value_as_int);
    $self->{gtkgramofile}->get_value('process_params','double_median_second_num_samples', $spin2->get_value_as_int);
    $self->{gtkgramofile}->{gui}->{double_median_filter_button}->set_sensitive(1);
    $window->destroy;
  });
  $dialog->show_all;
}

sub on_simple_mean_filter_clicked {
  my $widget = shift;
  my $self = shift;

  $self->on_generic_1par_filter_clicked('simple_mean_filter_button', "Simple Mean Filter Properties",
    "Number of samples to take the mean of", 'simple_mean_num_samples', 1, 101, 2);
}

sub on_rms_filter_clicked {
  my $widget = shift;
  my $self = shift;

  $self->on_generic_1par_filter_clicked('rms_filter_button', "RMS Filter Properties",
    "Number of samples to compute RMS of", 'rms_num_samples', 1, 101, 2);
}

sub on_cond_median_filter_clicked {
  my $widget = shift;
  my $self = shift;

  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};
  $self->{gtkgramofile}->{gui}->{cond_median_filter_button}->set_sensitive(0);

  my $message = "Conditional Median Filter Properties";
  my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                  'destroy-with-parent',
                                  'gtk-ok' => 'ok',
                                  'gtk-refresh' => 9,
                                  'gtk-cancel' => 'cancel');
  $dialog->vbox->add(Gtk2::Label->new($message));
  $dialog->vbox->add(Gtk2::HSeparator->new);
  my $spin1 = $self->label_and_spin($dialog, "Number of samples for median to interpolate ticks", 'cmf_median_tick_num_samples', 1, 101, 2);
  my $spin2 = $self->label_and_spin($dialog, "Length of the RMS operation (samples)", 'cmf_rms_length', 1, 101, 2);
  my $spin3 = $self->label_and_spin($dialog, "Length of the recursive median operation (samples)", 'cmf_recursive_median_length', 1, 101, 2);
  my $spin4 = $self->label_and_spin($dialog, "Decimation factor for the recursive median", 'cmf_decimation_factor', 1, 101, 1);
  my $spin5 = $self->label_and_spin($dialog, "Threshold for tick detection (thousandths)", 'cmf_tick_detection_threshold', 1000, 5000, 1);
  # Ensure that the dialog box is destroyed when the user responds.
  $dialog->signal_connect (response => sub { 
    my ($window, $response) = @_;
    if ($response eq "ok") {
      $self->{gtkgramofile}->set_value('process_params','cmf_median_tick_num_samples', $spin1->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf_rms_length', $spin2->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf_recursive_median_length', $spin3->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf_decimation_factor', $spin4->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf_tick_detection_threshold', $spin5->get_value_as_int);
    } elsif ($response ne "cancel") {
      $self->{gtkgramofile}->set_default('process_params','cmf_median_tick_num_samples');
      $self->{gtkgramofile}->set_default('process_params','cmf_rms_length');
      $self->{gtkgramofile}->set_default('process_params','cmf_recursive_median_length');
      $self->{gtkgramofile}->set_default('process_params','cmf_decimation_factor');
      $self->{gtkgramofile}->set_default('process_params','cmf_tick_detection_threshold');
    }
    $self->{gtkgramofile}->{gui}->{cond_median_filter_button}->set_sensitive(1);
    $window->destroy;
  });
  $dialog->show_all;
}

sub on_cond_median2_filter_clicked {
  my $widget = shift;
  my $self = shift;

  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};
  $self->{gtkgramofile}->{gui}->{cond_median2_filter_button}->set_sensitive(0);

  my $message = "Conditional Median Filter II Properties";
  my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                  'destroy-with-parent',
                                  'gtk-ok' => 'ok',
                                  'gtk-refresh' => 9,
                                  'gtk-cancel' => 'cancel');
  $dialog->vbox->add(Gtk2::Label->new($message));
  $dialog->vbox->add(Gtk2::HSeparator->new);
  my $spin1 = $self->label_and_spin($dialog, "Length of the RMS operation (samples)", 'cmf2_rms_length', 1, 101, 2);
  my $spin2 = $self->label_and_spin($dialog, "Length of the recursive median operation (samples)", 'cmf2_recursive_median_length', 1, 101, 2);
  my $spin3 = $self->label_and_spin($dialog, "Decimation factor for the recursive median", 'cmf2_decimation_factor', 1, 101, 1);
  my $spin4 = $self->label_and_spin($dialog, "Fine threshold for tick start/end (thousandths)", 'cmf2_tick_fine_threshold', 1, 5000, 1);
  my $spin5 = $self->label_and_spin($dialog, "Threshold for detection of tick presence (thousandths)", 'cmf2_tick_detection_threshold', 1000, 18000, 1);
  # Ensure that the dialog box is destroyed when the user responds.
  $dialog->signal_connect (response => sub { 
    my ($window, $response) = @_;
    if ($response eq "ok") {
      $self->{gtkgramofile}->set_value('process_params','cmf2_rms_length', $spin1->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf2_recursive_median_length', $spin2->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf2_decimation_factor', $spin3->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf2_tick_fine_threshold', $spin4->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf2_tick_detection_threshold', $spin5->get_value_as_int);
    } elsif ($response ne "cancel") {
      $self->{gtkgramofile}->set_default('process_params','cmf2_rms_length');
      $self->{gtkgramofile}->set_default('process_params','cmf2_recursive_median_length');
      $self->{gtkgramofile}->set_default('process_params','cmf2_decimation_factor');
      $self->{gtkgramofile}->set_default('process_params','cmf2_tick_fine_threshold');
      $self->{gtkgramofile}->set_default('process_params','cmf2_tick_detection_threshold');
    }
    $self->{gtkgramofile}->{gui}->{cond_median2_filter_button}->set_sensitive(1);
    $window->destroy;
  });
  $dialog->show_all;
}

sub on_cond_median3_filter_clicked {
  my $widget = shift;
  my $self = shift;

  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};
  $self->{gtkgramofile}->{gui}->{cond_median3_filter_button}->set_sensitive(0);

  my $message = "Conditional Median Filter IIF Properties";
  my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                  'destroy-with-parent',
                                  'gtk-ok' => 'ok',
                                  'gtk-refresh' => 9,
                                  'gtk-cancel' => 'cancel');
  $dialog->vbox->add(Gtk2::Label->new($message));
  $dialog->vbox->add(Gtk2::HSeparator->new);
  my $spin1 = $self->label_and_spin($dialog, "Length of the RMS operation (samples)", 'cmf3_rms_length', 1, 101, 2);
  my $spin2 = $self->label_and_spin($dialog, "Length of the recursive median operation (samples)", 'cmf3_recursive_median_length', 1, 101, 2);
  my $spin3 = $self->label_and_spin($dialog, "Decimation factor for the recursive median", 'cmf3_decimation_factor', 1, 101, 1);
  my $spin4 = $self->label_and_spin($dialog, "Fine threshold for tick start/end (thousandths)", 'cmf3_tick_fine_threshold', 1, 5000, 1);
  my $spin5 = $self->label_and_spin($dialog, "Threshold for detection of tick presence (thousandths)", 'cmf3_tick_detection_threshold', 1000, 18000, 1);
  my $spin6 = $self->label_and_spin($dialog, "Length for fft to interpolate (2^n)", 'cmf3_fft_length', 6, 12, 1);
  # Ensure that the dialog box is destroyed when the user responds.
  $dialog->signal_connect (response => sub { 
    my ($window, $response) = @_;
    if ($response eq "ok") {
      $self->{gtkgramofile}->set_value('process_params','cmf3_rms_length', $spin1->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf3_recursive_median_length', $spin2->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf3_decimation_factor', $spin3->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf3_tick_fine_threshold', $spin4->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf3_tick_detection_threshold', $spin5->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','cmf3_fft_length', $spin6->get_value_as_int);
    } elsif ($response ne "cancel") {
      $self->{gtkgramofile}->set_default('process_params','cmf3_rms_length');
      $self->{gtkgramofile}->set_default('process_params','cmf3_recursive_median_length');
      $self->{gtkgramofile}->set_default('process_params','cmf3_decimation_factor');
      $self->{gtkgramofile}->set_default('process_params','cmf3_tick_fine_threshold');
      $self->{gtkgramofile}->set_default('process_params','cmf3_tick_detection_threshold');
      $self->{gtkgramofile}->set_default('process_params','cmf3_fft_length');
    }
    $self->{gtkgramofile}->{gui}->{cond_median3_filter_button}->set_sensitive(0);
    $window->destroy;
  });
  $dialog->show_all;
}

sub on_simple_normalize_filter_clicked {
  my $widget = shift;
  my $self = shift;

  $self->on_generic_1par_filter_clicked('simple_normalize_filter_button', "Simple Normalize Filter Properties",
    "Enter normalize factor - Increase or reduce signal by 0 to +/- 100 %", 'simple_normalize_num_samples', 0, 100, 1);
}

sub on_start_process_clicked {
  my $widget = shift;
  my $self = shift;
  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};

  $self->{gtkgramofile}->set_value('process_general','process_stopped', 0);
  $self->{gtkgramofile}->{gui}->{stop_process_button}->set_sensitive(1);

  my $filter_list_ref = [];
  foreach my $key ($self->{gtkgramofile}->get_section_keys('process_filters')) {
    push @$filter_list_ref, $key if $self->{gtkgramofile}->get_value('process_filters',$key);
  }

  my $in_file_list_ref = [];
  my $out_file_list_ref = [];
  my $infiledir = $self->{gtkgramofile}->get_value('process_general','process_infile');
  my $outfiledir = $self->{gtkgramofile}->get_value('process_general','process_outfile');
  if (-f $infiledir) {
    push @$in_file_list_ref, $infiledir;
    if (-f $outfiledir) {
      push @$out_file_list_ref, $outfiledir;
    } elsif (-d $outfiledir) {
      my $filename = basename $infiledir;
      my $outfile = join "/", $outfiledir, $filename;
      push @$out_file_list_ref, $outfile;
    } else {
      my $message = $outfiledir  . ' does not exist';
      my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                      'destroy-with-parent',
                                      'gtk-ok' => 'none');
      my $label = Gtk2::Label->new ($message);
      $dialog->vbox->add ($label);
      $dialog->signal_connect (response => sub { $_[0]->destroy });
      $self->{gtkgramofile}->{gui}->{stop_process_button}->set_sensitive(0);
      $dialog->show_all;
      return;
    }
  } elsif (-d $infiledir) {
    my $infile_filter = $self->{gtkgramofile}->get_value('process_params','process_infile_filter');
    my $outfile_filter = $self->{gtkgramofile}->get_value('process_params','process_outfile_filter');
    my $d = DirHandle->new($infiledir);
    if (defined $d) {
      while (defined($_ = $d->read)) {
        next if (/^\.\.?$/);
        next if ($infile_filter and not /$infile_filter/);
        my $infile = join "/", $infiledir, $_;
        push @$in_file_list_ref, $infile;
        my $outfile;
        if (-d $outfiledir) {
          $outfile = join "/", $outfiledir, $_;
          if ($outfile_filter) {
            if ($self->{gtkgramofile}->get_value('process_params','process_op_regexp')) {
              my $regexp = $outfile_filter;
              $_ =~ $regexp;
              $outfile = join "/", $outfiledir, $_;
            } else {
              $outfile = join "/", $outfiledir, $_ . $outfile_filter;
            }
          }
          push @$out_file_list_ref, $outfile;
        } else {
          if ($outfile_filter) {
            if ($self->{gtkgramofile}->get_value('process_params','process_op_regexp')) {
              my $regexp = $outfile_filter;
              $_ =~ $regexp;
              $outfile = join "/", $infiledir, $_;
            } else {
              $outfile = join "/", $infiledir, $_ . $outfile_filter;
            }
            push @$out_file_list_ref, $outfile;
          } else {
            my $message = $outfiledir  . ' does not exist';
            my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                            'destroy-with-parent',
                                            'gtk-ok' => 'none');
            my $label = Gtk2::Label->new ($message);
            $dialog->vbox->add ($label);
            $dialog->signal_connect (response => sub { $_[0]->destroy });
            $dialog->show_all;
            $self->{gtkgramofile}->{gui}->{stop_process_button}->set_sensitive(0);
            return;
          }
        }
      }
      undef $d;
    }
  } else {
    my $message = $infiledir  . ' does not exist';
    my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                    'destroy-with-parent',
                                    'gtk-ok' => 'none');
    my $label = Gtk2::Label->new ($message);
    $dialog->vbox->add ($label);

    # Ensure that the dialog box is destroyed when the user responds.
    $dialog->signal_connect (response => sub { $_[0]->destroy });
    $dialog->show_all;
    $self->{gtkgramofile}->{gui}->{stop_process_button}->set_sensitive(0);
    return;
  }

  my $start_time = sprintf("%02d:%02d:%02d.%03d",
                           $self->{gtkgramofile}->get_value('process_params','start_hours'),
			   $self->{gtkgramofile}->get_value('process_params','start_minutes'),
			   $self->{gtkgramofile}->get_value('process_params','start_seconds'),
			   $self->{gtkgramofile}->get_value('process_params','start_thousandths'));

  my $end_time = sprintf("%02d:%02d:%02d.%03d",
                         $self->{gtkgramofile}->get_value('process_params','end_hours'),
			 $self->{gtkgramofile}->get_value('process_params','end_minutes'),
			 $self->{gtkgramofile}->get_value('process_params','end_seconds'),
			 $self->{gtkgramofile}->get_value('process_params','end_thousandths'));

  $self->{gtkgramofile}->{logic}->process_signal($in_file_list_ref, $out_file_list_ref, $filter_list_ref,
                 $self->{gtkgramofile}->get_value('process_params','simple_median_num_samples'),
                 $self->{gtkgramofile}->get_value('process_params','double_median_first_num_samples'),
                 $self->{gtkgramofile}->get_value('process_params','double_median_second_num_samples'),
                 $self->{gtkgramofile}->get_value('process_params','simple_mean_num_samples'),
                 $self->{gtkgramofile}->get_value('process_params','rms_filter_num_samples'),
                 $self->{gtkgramofile}->get_value('process_params','cmf_median_tick_num_samples'),
                 $self->{gtkgramofile}->get_value('process_params','cmf_rms_length'),
                 $self->{gtkgramofile}->get_value('process_params','cmf_recursive_median_length'),
                 $self->{gtkgramofile}->get_value('process_params','cmf_decimation_factor'),
                 $self->{gtkgramofile}->get_value('process_params','cmf_tick_detection_threshold'),
                 $self->{gtkgramofile}->get_value('process_params','cmf2_rms_length'),
                 $self->{gtkgramofile}->get_value('process_params','cmf2_recursive_median_length'),
                 $self->{gtkgramofile}->get_value('process_params','cmf2_decimation_factor'),
                 $self->{gtkgramofile}->get_value('process_params','cmf2_tick_fine_threshold'),
                 $self->{gtkgramofile}->get_value('process_params','cmf2_tick_detection_threshold'),
                 $self->{gtkgramofile}->get_value('process_params','cmf3_rms_length'),
                 $self->{gtkgramofile}->get_value('process_params','cmf3_recursive_median_length'),
                 $self->{gtkgramofile}->get_value('process_params','cmf3_decimation_factor'),
                 $self->{gtkgramofile}->get_value('process_params','cmf3_tick_fine_threshold'),
                 $self->{gtkgramofile}->get_value('process_params','cmf3_tick_detection_threshold'),
                 $self->{gtkgramofile}->get_value('process_params','cmf3_fft_length'),
                 $self->{gtkgramofile}->get_value('process_params','simple_normalize_factor'),
		 $self->{gtkgramofile}->{gui}->{begin_and_end_times_radio}->get_active,
		 $start_time,
		 $end_time,
                 $self->{gtkgramofile}->{gui}->{whole_frames_check}->get_active,
                 $self->{gtkgramofile}->get_value('process_params','frame_size'),
		 );
}

sub on_stop_process_clicked {
  my $widget = shift;
  my $self = shift;

  $self->on_stop_generic_clicked('process_general', 'process_pid_file', 
    'process_stopped', 'process_cancel_button');
}

sub on_save_clicked {
  my $widget = shift;
  my $self = shift;

  $self->{gtkgramofile}->{gui}->message("Settings saved");
  $self->{gtkgramofile}->save_settings;
}

sub times_dialog {
  my $self = shift;

  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};
  my $message = "Enter Begin and End times to process";
  my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                  'destroy-with-parent',
                                  'gtk-ok' => 'ok',
                                  'gtk-refresh' => 9,
                                  'gtk-cancel' => 'cancel');
  $dialog->vbox->add(Gtk2::Label->new($message));
  $dialog->vbox->add(Gtk2::HSeparator->new);
  my $end_times = $self->get_end_times;
  my $end_list = [qw(11 59 59 999)];
  foreach my $num (0 .. @$end_times) {
    if ($end_times->[$num]) {
      $end_list->[$num] = $end_times->[$num];
      last;
    } else {
      $end_list->[$num] = 0;
    }
  }
    
  my $spin1 = $self->label_and_spin($dialog, "Start hours", 'start_hours', 0, $end_list->[0], 1);
  my $spin2 = $self->label_and_spin($dialog, "Start minutes", 'start_minutes', 0, $end_list->[1], 1);
  my $spin3 = $self->label_and_spin($dialog, "Start seconds", 'start_seconds', 0, $end_list->[2], 1);
  my $spin4 = $self->label_and_spin($dialog, "Start thousandths", 'start_thousandths', 0, $end_list->[3], 1);
  my $spin5 = $self->label_and_spin($dialog, "End hours", 'end_hours', 0, $end_list->[0], 1, $end_times->[0]);
  my $spin6 = $self->label_and_spin($dialog, "End minutes", 'end_minutes', 0, $end_list->[1], 1, $end_times->[1]);
  my $spin7 = $self->label_and_spin($dialog, "End seconds", 'end_seconds', 0, $end_list->[2], 1, $end_times->[2]);
  my $spin8 = $self->label_and_spin($dialog, "End thousandths", 'end_thousandths', 0, $end_list->[3], 1, $end_times->[3]);

  $dialog->signal_connect (response => sub {
    my ($window, $response) = @_;
    if ($response eq "ok") {
      $self->{gtkgramofile}->set_value('process_params','start_hours', $spin1->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','start_minutes', $spin2->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','start_seconds', $spin3->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','start_thousandths', $spin4->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','end_hours', $spin5->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','end_minutes', $spin6->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','end_seconds', $spin7->get_value_as_int);
      $self->{gtkgramofile}->set_value('process_params','end_thousandths', $spin8->get_value_as_int);
    } elsif ($response ne "cancel") {
      $self->{gtkgramofile}->set_default('process_params','start_hours');
      $self->{gtkgramofile}->set_default('process_params','start_minutes');
      $self->{gtkgramofile}->set_default('process_params','start_seconds');
      $self->{gtkgramofile}->set_default('process_params','start_thousandths');
      $self->{gtkgramofile}->set_default('process_params','end_hours');
      $self->{gtkgramofile}->set_default('process_params','end_minutes');
      $self->{gtkgramofile}->set_default('process_params','end_seconds');
      $self->{gtkgramofile}->set_default('process_params','end_thousandths');
    }
    $window->destroy;
  });

  $dialog;
}

sub on_begin_and_end_times_radio_clicked {
  my $widget = shift;
  my $self = shift;

  if ($self->{gtkgramofile}->{gui}->{begin_and_end_times_radio}->get_active) {
    my $dialog = (defined $self->{times_dialog}) ? $self->{times_dialog} : $self->set_times_dialog;
    $dialog->show_all;
  } else {
    $self->{times_dialog}->hide;
    $self->{times_dialog} = undef;
    $self->{gtkgramofile}->set_default('process_params','start_hours');
    $self->{gtkgramofile}->set_default('process_params','start_minutes');
    $self->{gtkgramofile}->set_default('process_params','start_seconds');
    $self->{gtkgramofile}->set_default('process_params','start_thousandths');
    $self->{gtkgramofile}->set_default('process_params','end_hours');
    $self->{gtkgramofile}->set_default('process_params','end_minutes');
    $self->{gtkgramofile}->set_default('process_params','end_seconds');
    $self->{gtkgramofile}->set_default('process_params','end_thousandths');
  }
}

sub frame_dialog {
  my $self = shift;
  my $setting = shift;
  my $button = shift;
  my $windowname = shift; 
  my $labeltext = shift; 
  my $filename = shift; 
  my $filename_entry = shift; 

  my $parent = $self->{gtkgramofile}->{gui}->{gramofile};
  my $message = "Enter Frame Size";
  my $entry = Gtk2::Entry->new;
  $entry->set_text($self->{gtkgramofile}->get_default('process_params','frame_size'));
  my $dialog = Gtk2::Dialog->new ( $message, $parent,
                                  'destroy-with-parent',
                                  'gtk-ok' => 'ok',
                                  'gtk-refresh' => 9,
                                  'gtk-cancel' => 'cancel');
  $dialog->vbox->add(Gtk2::Label->new($message));
  $dialog->vbox->add(Gtk2::HSeparator->new);
  $dialog->vbox->add($entry);

  $dialog->signal_connect (response => sub {
    my ($window, $response) = @_;
    if ($response eq "ok") {
      $self->{gtkgramofile}->set_value('process_params','frame_size', $entry->get_text);
    } elsif ($response ne "cancel") {
      $self->{gtkgramofile}->set_default('process_params','frame_size');
    }
    $window->destroy;
  });

  $dialog;
}

sub on_whole_frames_check_clicked {
  my $widget = shift;
  my $self = shift;

  if ($self->{gtkgramofile}->{gui}->{whole_frames_check}->get_active) {
    my $dialog = (defined $self->{frame_dialog}) ? $self->{frame_dialog} : $self->set_frame_dialog;
    $dialog->show_all;
  } else {
    $self->{frame_dialog}->hide;
    $self->{frame_dialog} = undef;
    $self->{gtkgramofile}->set_default('process_params','frame_size');
  }
}

sub get_end_times {
  my $self = shift;

  my $end_list = [qw(11 59 59 999)];
  my $wav_file = $self->{gtkgramofile}->get_value('process_general','process_infile');
  
  return $end_list unless (-e $wav_file and -f _);

  my $tracks_file = $wav_file . ".tracks";
  return $end_list unless (-e $tracks_file and -f _);

  my $end; 
  my $tracks_fh = IO::File->new($tracks_file, "r") 
    or croak "Can't read from $tracks_file, $!";
  while (<$tracks_fh>) {
    $end=$_ if (s/^Track\d+end=//);
  } 
  chomp $end; 
  $end_list = [ $end =~ /^(\d+):(\d+):(\d+)\.(\d+)$/ ];
  return $end_list;
}

1;