#!/usr/bin/perl -w

# sphinx2-demo -- wrapper for sphinx demo
#
# 02-Aug-1999 Kevin A. Lenzo <lenzo@cs.cmu.edu> at Carnegie Mellon University
#             Created.
#

use strict;

$| = 1;  # turn off STDOUT buffering 

print "\n";
print "sphinx2-demo:\n";
print "  Simple real-time demo of CMU Sphinx2 using a toy grammar.\n";
print "\n";

my ($pgm, $beamwidth, $output, $ctloffset, $ctlcount) = @ARGV;
my $arch;

my $s2root;

BEGIN {
  $arch = `uname -m`.'_'.`uname -s`;
  $arch =~ s/\s+//g;
  $arch =~ s/i.86/i386/i;
  $arch =~ tr/A-Z/a-z/;

  $s2root = "/usr/share/sphinx2";
  die "please set \$s2root: $!" unless -d $s2root;
  
  push @INC, "/usr/bin", 
	"$s2root/bin", "$s2root/src/build/$arch/bin";
}

my $task = "$s2root/model/lm/turtle";
@ARGV and $task = shift @ARGV;

die "don't know how to do '$task' and cant find $task directory: $!" 
  unless -d $task;

$pgm = "/usr/bin/sphinx2-continuous";

if (! -x $pgm) {
  foreach (@INC) {
    if (-x "$_/$pgm") {
      $pgm = "$_/$pgm";
      last;
    }
  }
}

die "this requires $pgm to execute!" unless -x $pgm;

$beamwidth = "narrow";
$output = $task;

my $sex = "g";
my $target_command; # = 'cat >> log';
my $matchfile; #  = "$output.match";

my $kbdumpdir = $s2root;

my ($lmfile, $dictfile, $phonefile, $mapfile, $ctlfile) = ('NOTFOUND') x 6;

my $file;
opendir DIR, $task or die "can't opendir $task: $!";
while ($file = readdir DIR) {
  $file = "$task/$file";
  next if -d $file;

  $file =~ /\.lm$/    and $lmfile    = $file;
  $file =~ /\.dic$/   and $dictfile  = $file;
  $file =~ /\.ctl$/   and $ctlfile   = $file;

#  $file =~ /\.phone$/ and $phonefile = $file;
#  $file =~ /\.map$/   and $mapfile   = $file;
}

closedir DIR;

my $cepdir    = "$task/ctl";

my $modeldir     = "$s2root/model/hmm/6k";
my $sendumpfile  = "$modeldir/sendump";
my $noisedict    = "$modeldir/noisedict";

$phonefile       = "$modeldir/phone";
$mapfile         = "$modeldir/map";

die "$modeldir: $!" unless -d $modeldir;
die "$sendumpfile: $!" unless -e $sendumpfile;
die "$phonefile: $!" unless -e $phonefile;
die "$mapfile: $!" unless -e $mapfile;
die "$lmfile: $!" unless -e $lmfile;
die "$dictfile: $!" unless -e $dictfile;
die "$noisedict: $!" unless -e $noisedict;

print "  The CMU Sphinx demo uses several resources:\n";
print "\n";
print "     task directory: $task\n";
print "                     A directory with the task resources in it; the\n";
print "                     language model and the dictionary in particular.\n";
print "\n";
print "     language model: $lmfile\n";
print "                     A language model. This describes the probability\n";
print "                     sequences of words, which helps guide the search.\n";
print "\n";
print "         dictionary: $dictfile\n";
print "                     A pronunciation dictionary that contains every word\n";
print "                     that is supposed to appear in the input.\n";
print "\n";
print "      HMM directory: $modeldir\n";
print "                     A directory containing the acoustic models,\n";
print "                     which are implemented as Hidden Markov Models (HMMs).\n";
print "                     In quirky CMU terms, each state is a senone, and\n";
print "                     there are five senones for each triphone HMM.\n";
print "\n";
print "     noiseword dict: $noisedict\n";
print "                     The noise word dictionary, if any. Sounds such as\n";
print "                     ++BREATH++, ++COUGH++, ++SMACK++, ++LAUGH++, ++UH++\n";
print "                     By convention, +BREATH+ is a noise phone and \n";
print "                     ++BREATH++ is a noise word pronounced +BREATH+\n";
print "\n";
print "  The grammar contains things like\n";
print "     GO FORWARD SEVEN METERS\n";
print "     GO FORWARD TEN METERS\n";
print "     GO BACKWARD\n";
print "     TURN LEFT NINETY DEGREES\n";
print "     EXIT\n";
print "  and so in made into a trigram language model.\n";
print "\n";
print "  Say things like \"go forward ten meters\"\n";
print "               or \"rotate right 45 degrees\"\n";
print "\n";
print "  Say  EXIT  or  QUIT  to quit.\n";
print "\n";

$ctloffset = 0 unless defined $ctloffset;
$ctlcount = 100000000 if (not defined $ctlcount or $ctlcount <= 0);

#
# Beamwidths: There are two sets, wide beam for accuracy, narrow beam for speed.
# 

my       $bw=2e-6;
my     $npbw=2e-6;
my     $lpbw=2e-5;
my $lponlybw=5e-4;
my     $nwbw=5e-4;
my      $top=1;
my $topsenfrm=3;
my $topsenthresh=-70000;
my $fwdflat="FALSE";

if (lc($beamwidth) eq "wide") {
  $bw=1e-6;
  $npbw=1e-6;
  $lpbw=1e-5;
  $lponlybw=3e-4;
  $nwbw=3e-4;
  $top=4;
  $topsenfrm=4;
  $topsenthresh=-80000;
  $fwdflat="TRUE";
}

my  $lw=6.5;
my  $uw=0.5;
my  $ip=0.65;
my  $fp=1e-10;
my  $sp=0.005;

my  $fwdflatbeam   = 1e-8;
my  $fwdflatnwbeam = 3e-4;
my  $fwdflatlw     = 8.5;
my  $bestpathlw    = 9.5;

unlink($matchfile) if $matchfile;

my $what = join " ", ($pgm,
       -live => "TRUE",
       -ctloffset => $ctloffset,
       -ctlcount => $ctlcount,
       -cepdir => $cepdir,
       -datadir => $cepdir,
#       -agcmax => "FALSE",  # only agcmax in batch mode
       -agcemax => "TRUE",   # only agcemax in live mode
# language weights, other LM parameters ------------- 
       -langwt => $lw,
       -fwdflatlw => $fwdflatlw,
       -rescorelw => $bestpathlw,
       -ugwt => $uw,
       -fillpen => $fp,
       -silpen => $sp,
       -inspen => $ip,
# fwdtree pass beamwidths --------------------------- 
       -top => $top,
       -topsenfrm => $topsenfrm,
       -topsenthresh =>  $topsenthresh,
       -beam => $bw,
       -npbeam => $npbw,
       -lpbeam => $lpbw,
       -lponlybeam => $lponlybw,
       -nwbeam => $nwbw,
# fwdflat pass beamwidths ---------------------------
       -fwdflat => $fwdflat,
       -fwdflatbeam => $fwdflatbeam,
       -fwdflatnwbeam => $fwdflatnwbeam,
# bestpath search ----------------------------------- 
       -bestpath => "TRUE",
#	-nbest => 200,		
# kb stuff (LM/dictionary/hmm files) ---------------- 
       -kbdumpdir => $kbdumpdir,
       -lmfn => $lmfile,
       -dictfn => $dictfile,
       -ndictfn => $noisedict,
       -phnfn => $phonefile,
       -mapfn => $mapfile,
       -hmmdir => $modeldir,
       -hmmdirlist => $modeldir,
       '-8bsen' => "TRUE",
       -sendumpfn => $sendumpfile,
       -cbdir => $modeldir,
# misc. ----------------------------------------------
# 	-backtrace => FALSE,
#       -matchfn => $matchfile,
       );

print "  Executing $pgm... (see sphinx2-simple for example)\n";
print "  See sphinx2-simple for example arguments, or edit this script.\n";
print "\n";

# uncomment the line below to see the whole argument list.
# print "# $what\n"; 

open SPHINX, "$what 2>\&1 |" or die "something failed: $!";
print "[initializing]\n";

my (%is, %are);

if ($target_command) {
  open TARGET, "|$target_command" or die "can't open $target_command: $!";
}

while (<SPHINX>) {
  # take the output of the $pgm binary and process it
  # this is usually sphinx2-continuous, and example binary
  # linked against the sphinx2 library.

  print $_ if 0; # make this if 1 to debug the output

  # some simple handlers
  /READY..../ and do { print "[silence] "; next };
  /Listening.../ and do { print "[audio] "; next };

  # handle the output results
  if (/^(\d+):\s+(.*?)\s*$/) {
      my ($frame, $result) = ($1, $2);
      $result = uc $result;
      print "$result\n";
      print TARGET "$result\n" if $target_command;

      $result =~ /^(exit|quit)$/i and do {
	  print "[exit]\n";
	  exit ;
      };
  }
}

close TARGET if $target_command;

$@ and print "error: $@\n";


