#!/usr/bin/perl -w

### earwhip.pl: play a random pitch, then popup windows saying what it was.

use strict;

my $usage = "Usage: earwhip.pl [options]\n"
    . "\n"
    . "Available options are\n"
    . "\n"
    . "   -u/--duration SECONDS   Play pitch for this long.\n"
    . "   -w/--wait SECONDS       Wait this long before showing answer.\n"
    . "   -s/--sleep SECONDS      Sleep this long between pitches.\n"
    . "   -m/--midi-player        Play the pitch using this program.\n"
    . "   -p/--print              Print answer to stdout (can use with -d).\n"
    . "   -d/--display DISPLAY    Put answer on DISPLAY in a GTK window.\n"
    . "   -1/--once               Run once and exit.  Incompatible with -s.\n"
    . "   -h/--help               Print this usage message.\n";


# All these settings can be changed on the command line:
my $duration   = 3;
my $popup_wait = 0;
my $sleep_time = -1;
my $once = 0;
my @displays = ();
my $midi_player = "help-timidity.sh";

# How to flash the answer afterwards (these are not mutually exclusive).
my $print_after = 0;
my $popup_after = 0;

&process_args ();

my @pitches     = ("a", "b", "c", "d", "e", "f", "g");
my @modifiers   = ("", "s", "f");     # natural, sharp, or flat
# my @octaves     = ("3", "4", "5", "6");
my @octaves     = ("5");

my @instruments = ("'Oboe'",
                   "'Tuba'",
                   "'Bagpipe'",
                   "'Viola'",
                   "'Vibraphone'",
                   "'Trumpet'",
                   "'Bassoon'",
                   "'Flute'",
                   "'Celesta'",
                   "'Clav'",
                   "'Piccolo'",
                   "'Cello'",
                   "'Contrabass'",
                   "'Glockenspiel'",
                   "'Trombone'",
                   "'Violin'",
                   "'Clarinet'",
                   "'Accordion'",
                   "'Recorder'",
                   "'Harpsichord'");

# my @instruments = ("'clarinet'");

sub process_args ()
{
  while (my $arg = shift (@ARGV)) 
  {
    if ($arg =~ /^-d$|^--display$/) {
      my $narg = shift (@ARGV) || die "$usage";
      push (@displays, $narg);
    }
    elsif ($arg =~ /^-m$|^--midi-player$/) {
      my $narg = shift (@ARGV) || die "$usage";
      $midi_player = "$narg";
    }
    elsif ($arg =~ /^-u$|^--duration$/) {
      $duration = shift (@ARGV) || die "$usage";
    }
    elsif ($arg =~ /^-w$|^--wait$/) {
      $popup_wait = shift (@ARGV) || die "$usage";
    }
    elsif ($arg =~ /^-s$|^--sleep$/) {
      $sleep_time = shift (@ARGV) || die "$usage";
    }
    elsif ($arg =~ /^-1$|^--once$/) {
      $once = 1;
    }
    elsif ($arg =~ /^-p$|^--print$/) {
      $print_after = 1;
    }
    elsif ($arg =~ /^-w$|^--popup$/) {
      $popup_after = 1;
    }
    elsif ($arg =~ /^-h$|^--help$/) {
      print "$usage";
      exit (0);
    }
    else {
      die ("$usage");
    }
  }

  if ($once && $sleep_time >= 0) {
    die ("-1 (--once) is incompatible with -s (--sleep).\n");
  }
}


sub do_it ()
{
  srand (time);
  
  my $pitch = $pitches[rand (scalar (@pitches))]
      . $modifiers[rand (scalar (@modifiers))]
          . $octaves[rand (scalar (@octaves))];
  
  my $instrument = $instruments[rand (scalar (@instruments))];
  
  # Perl/MIDI syntax can't handle white-key-to-white-key accidentals.
  # Go figure.
  $pitch =~ s/^ff/e/;    # F-flat   becomes  E
  $pitch =~ s/^bs/c/;    # B-sharp  becomes  C
  $pitch =~ s/^es/f/;    # E-sharp  becomes  F
  $pitch =~ s/^cf/b/;    # C-flat   becomes  B
  
  my $tmpfile = "/tmp/earwhip-midi.$$";
  my $make_midi_data_cmd 
      = "make-pitch.pl $pitch $duration $instrument > $tmpfile";
  my $play_midi_data_cmd = "$midi_player $tmpfile > /dev/null 2>&1";
  
  system ($make_midi_data_cmd);
  system ($play_midi_data_cmd);
  unlink ($tmpfile);
  
  # Reformat for human readability
  $pitch = ucfirst ($pitch);
  $pitch =~ s/^([A-Z])f/$1b/;
  $pitch =~ s/^([A-Z])s/$1\#/;
  $pitch =~ s/([^0-9]+)([0-9])/$1 $2/;
  
  my $human_string = "$pitch, ${instrument}";
  
  sleep ($popup_wait);

  if ($print_after) {
    print "${human_string}\n";
  }

  foreach my $disp (@displays) 
  {
    $ENV{'DISPLAY'} = "$disp";
    system ("gpopup '${human_string}' &");
  }
}


while (1)
{
  &do_it ();

  if ($once) {
    last;
  }

  if ($sleep_time > 0) {
    sleep ($sleep_time);
  }
  else {
    sleep (rand (180) + 300);
  }
}
