#!/usr/bin/perl
# consecutivelabels.pl: Mark Hasegawa-Johnson
# Illinois Speech and Language Engineering (ISLE group)
#
# Usage (printed if consecutivelabels.pl is called with any arguments):
#
$USAGE=<<ENDUSAGE;

CONSECUTIVELABELS.PL: Separate a string of labels into consecutive lines

Usage:
 consecutivelabels.pl < input.mlf > output.mlf
Example:
 consecutivelabels.pl < icsi-syl.mlf > pseudo-phn.mlf

This script assumes that a line in input.mlf with several labels is
intended to represent multiple consecutive segments.  The segments are
split into their own lines.  The time allocated to the original
sequence is divided uniformly among the labels.  Thus for example the
following line:

  1300000 1600000 l ay n

becomes:

  1300000 1400000 l
  1400000 1500000 ay
  1500000 1600000 n
  
ENDUSAGE

####################################################################
# GLOBAL VARIABLES

# Conversion rates for various file formats
$MLFPERIOD = 0.0000001;

# Math functions
sub abs { my($x)=shift; ($x>0) ? $x : -$x ; }

###################################################
# Subroutine to read MLF entry
#
# Syntax: $data = read_MLF_entry("filename", \*INPUTFILE);
#
# Returned value $data is an arrayref of arrayrefs
# $$tran[0][0] is first seg start time, $$tran[0][1] is end time,
# @$$tran[0][2..$#{$$tran[0]}] is the sequence of labels at first time.
#
sub read_MLF_entry {
  my($filename) = $_[0];
  my($fh) = $_[1];
  my(@tran) = ();
  my(@labels) = ();
  
  # Read all continuous lines that begin with a char other than period
  while(($l = <$fh>) =~ /^[^\.]/) {
    # Option 1: line is in "time time labels" format
    if ($l=~/(\d+)\s+(\d+)\s+(.*)/ ) {
      @labels = split(/\s+/,$3);
      push(@tran, [ $1 * $MLFPERIOD, $2 * $MLFPERIOD, @labels ] );
    }
    # Option 2: line contains any non-whitespace labels, but no times
    elsif ($l =~ /\S/) {
      @labels = split(/\s+/,$l);
      push(@tran, [ 0, 0, @labels ] );
    }
  }
  return(\@tran);
}

###################################################
# Separate consecutive labels on a line, so that each label
#  gets its own separate line
#
# Syntax: $t0 = consecutivelabels($t1)
#
# $t1 and $t0 are arrayref arrayrefs.
# $$t1[0][0] is the first start time, $$t1[0][1] is first end time.
# $$t1[0][2..$#{$$t1[0]}] is the sequence of labels at first time.
#
sub consecutivelabels {
  my($t1) = $_[0];
  my(@t0) = ();
  my($r) = [];

  foreach $w (@$t1) {
    # How many labels are on this line?
    my($nlabels) = @$w - 2;
    
    # Divide the time into $nlabels uniform segments
    my($start_time) = $$w[0];
    my($delta_t) = ($$w[1] - $$w[0])/$nlabels;
    
    # Create an output line for each label on the line
    foreach $label (@$w[2..$#$w]) {
      push(@t0, [ $start_time, $start_time+$delta_t, $label ]);
      $start_time += $delta_t;
    }
  }
  return(\@t0);
}

###################################################
# Print phoneme data to an MLF file
#
# Syntax: print_MLF_entry("root_filename", \@phndata, $filehandleref);
#
# The output filehandle MLF must be globally defined
# and already open.
#
sub print_MLF_entry {
  my($filename) = $_[0];
  my($phndata) = $_[1];
  my($fh) = $_[2];

  # Print the filename to the MLF
  $filename =~ s:.*/::g;
  $filename =~ s/\..*//g;
  print $fh "\"*/$filename\.lab\"\n";

  # Print out the converted data into MLF, and to the label file
  foreach $r (@$phndata) {
    my(@l) = @$r[2..$#$r];
    print $fh sprintf("%d %d ",$$r[0]/$MLFPERIOD,$$r[1]/$MLFPERIOD),"@l\n";
  }
  # Print final period that separates sentences
  print $fh '.',"\n";
}

###########################################
# Main Program

if (@ARGV>0) { print $USAGE; exit; }

# Print MLF header to STDOUT
print '#!MLF!#', "\n";

# Extract from STDIN
while (<STDIN>) {
  # If this line starts a transcription, read it
  if (/\".*\/([^\/\.]+)[^\/]*\"/) {
    my($filename) = $1;
    $itrans = read_MLF_entry($filename,\*STDIN);
    $otrans = consecutivelabels($itrans);
    print STDERR "$filename(0..$#$itrans) -> $filename(0..$#$otrans)\n";
  
    # Print out the final merged MLF
    print_MLF_entry($filename, $otrans, \*STDOUT);
  }
}

