#! /usr/bin/perl
#
# Special purpose program for Unix/Linux:
# Read all arguments as aligned praat label files with only a phoneme tier
# and align the original label file with it
# Skips all files that already exist in the target directory
#
# Use:
# The aligned label files are in ./Labels/<SpeakerID>/ASPEX
# The original label files are in ./Labels/<SpeakerID>/prephoneme
# The results will be placed in: ./Labels/<SpeakerID>/phoneme
#
# Call:
# ./AlignAutomaticLabeling.pl ./Labels/F28G/ASPEX/F28G1FPA*
#
# This will convert all files in F28G whose names start with 'F28G1FPA'
#
#    Copyright (C) 2000, 2001  R.J.J.H. van Son
# 
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
# 
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
# 
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
require 'SentenceLabel.pl';

#
#
# Do not execute inside a require
unless(caller())
{
    # Define the sonority of phonemes
    my %Sonority = (
'p' => '1',
'b' => '2',
't' => '1',
'd' => '2',
'k' => '1',
'g' => '2',
'f' => '3',
'v' => '4',
's' => '0',
'z' => '4',
'S' => '3',
'Z' => '4',
'x' => '3',
'G' => '4',
'h' => '1',
'N' => '5',
'm' => '5',
'n' => '5',
'J' => '6',
'l' => '6',
'r' => '6',
'w' => '7',
'j' => '7',
'I' => '8',
'E' => '8',
'A' => '8',
'O' => '8',
'Y' => '7',
'i' => '7',
'y' => '7',
'e' => '8',
'2' => '8',
'a' => '9',
'o' => '8',
'u' => '7',
'@' => '6',
'9' => '8',
'E+' => '8',
'9+' => '8',
'O+' => '8',
'E:' => '8',
'9:' => '7',
'O:' => '8',
'E~' => '8',
'A~' => '8',
'O~' => '8',
'Y~' => '7',

'I:' => '8',
'A:' => '8',
'Y:' => '7',
'i:' => '7',
'y:' => '7',
'e:' => '8',
'2:' => '8',
'a:' => '9',
'o:' => '8',
'u:' => '7',
'@:' => '6'
);
    my %PhonemeClass = (
'p' => 'Plosive',
'b' => 'Plosive',
't' => 'Plosive',
'd' => 'Plosive',
'k' => 'Plosive',
'g' => 'Plosive',
'f' => 'Fricative',
'v' => 'Fricative',
's' => 'Special',
'z' => 'Fricative',
'S' => 'Fricative',
'Z' => 'Fricative',
'x' => 'Fricative',
'G' => 'Fricative',
'h' => 'Fricative',
'N' => 'Nasal',
'm' => 'Nasal',
'n' => 'Nasal',
'J' => 'Nasal',
'l' => 'Semivowel',
'r' => 'Semivowel',
'w' => 'Semivowel',
'j' => 'Semivowel',
'I' => 'Vowel',
'E' => 'Vowel',
'A' => 'Vowel',
'O' => 'Vowel',
'Y' => 'Vowel',
'i' => 'Vowel',
'y' => 'Vowel',
'e' => 'Vowel',
'2' => 'Vowel',
'a' => 'Vowel',
'o' => 'Vowel',
'u' => 'Vowel',
'@' => 'Vowel',
'E+' => 'Vowel',
'9+' => 'Vowel',
'O+' => 'Vowel',
'E:' => 'Vowel',
'9:' => 'Vowel',
'O:' => 'Vowel',
'E~' => 'Vowel',
'A~' => 'Vowel',
'O~' => 'Vowel',
'Y~' => 'Vowel',

'I:' => 'Vowel',
'A:' => 'Vowel',
'Y:' => 'Vowel',
'i:' => 'Vowel',
'y:' => 'Vowel',
'e:' => 'Vowel',
'2:' => 'Vowel',
'a:' => 'Vowel',
'o:' => 'Vowel',
'u:' => 'Vowel',
'@:' => 'Vowel'
);

    # Classes that can/not be combined syllable initial
    my %NotInitialRules = (
'Nasal-Semivowel' => 1,
'N-Vowel' => 1,
'Plosive-m' => 1,
'x-Nasal' => 1,
'h-Vowel' => 0,
'J-Vowel' => 0,
'Special-Plosive' => 0,
'Special-Fricative' => 0,
'Special-Nasal' => 0,
'Special-Semivowel' => 0,
);

	  # Define and initialize
    my @GlobFileList = @ARGV;
    my $FilePattern;

    foreach $FilePattern (@GlobFileList)
    {
        # Get the real filenames (expand * and ?)
        my @FileList = glob("$FilePattern");
        my $FileName;

        # Handle All files
        foreach $FileName (@FileList)
        {
        	my $LabelError = "";
        	
        	# Take apart the file path to get the relevant directories and ID numbers
        	# e.g., "./Labels/F28G/ASPEX/F28G1FPA1VCVA_rvs.aspex"
        	# -> $`=".", $1="Labels", $2="F28G", $3="ASPEX", $4="F28G1FPA1VCVA" , $5="aspex"
        	$FileName =~ m@/([\w]+)/([\w]+)/([^/\.\_]+)_([\w]+)\.([\w]+)$@i;
        	my $HomeDirectory = $`;
        	my $SpeakerID = $1;
        	my $AspexDirectory = $2;
        	my $ItemID = $3;
        	my $Labeler = $4;
         	my $LabelFileType = $5;
        	
        	# Construct the filenames
        	# First, the name (and path) of the Labelfile
        	my $AspexLabelFileName = $FileName;
        	my $LabelFileName = "$HomeDirectory/$SpeakerID/prephoneme/${ItemID}.prephoneme";
        	my $TargetFileName = "$HomeDirectory/$SpeakerID/phoneme/${ItemID}_${Labeler}.phoneme";
        	my $TranslitFileName = "$HomeDirectory/$SpeakerID/translit/${ItemID}.translit";
        	
        	# Do NOT change existing labelfiles
        	next if -s $TargetFileName;

        	# Read the Aligned labelfile
        	my $AspexLabelFile = new SentenceLabel;
        	$AspexLabelFile->FoldedIDcodes('PHONEMES');
        	$AspexLabelFile->ReadLabelFile("$AspexLabelFileName");
        	
        	# Normalize labels
        	$AspexLabelFile->map('PHONEMES', '$_ =~ s/sil/\*/g');
        	$AspexLabelFile->map('PHONEMES', '$_ =~ s/^\s*[0]\s*$/\*/ig');
        	$AspexLabelFile->map('PHONEMES', '$_ = (/\S/ ? $_ : "\*");');
        	$AspexLabelFile->map('PHONEMES', '$_ =~ s/\://ig');
        	$AspexLabelFile->map('PHONEMES', '$_ =~ s/Ei/E+/ig');
        	$AspexLabelFile->map('PHONEMES', '$_ =~ s/9y/9+/ig');
        	$AspexLabelFile->map('PHONEMES', '$_ =~ s/Y[\+]/9+/ig');
        	$AspexLabelFile->map('PHONEMES', '$_ =~ s/Ou/O+/ig');
        	# Expand fused phonemes
        	$AspexLabelFile->map('PHONEMES', '$_ =~ s/([A-Z0-9\@][\+\:\~]?)(?=[A-Z0-9\@])/\1 /ig');
        	
        	# Remove double silences
        	$AspexLabelFile->presetItem('PHONEMES');
        	while($AspexLabelFile->nextItem('PHONEMES') > -1)
        	{
        	      next unless $AspexLabelFile->currentValue('PHONEMES') =~ /^([\*\#]+|sil)$/;
        	
        	      # Remove double silences
        	      $AspexLabelFile->removeItem('PHONEMES')
        	         if $AspexLabelFile->readNextValue('PHONEMES') =~ /^([\*\#]+|sil)$/;
        	};
        	
        	# Read the original labelfile
        	my $OrigLabelFile = new SentenceLabel;
        	$OrigLabelFile->FoldedIDcodes('SENTENCE', 'TRANSLIT', 'WORDS', 'SYLLABLES', 'PHONEMES');
        	$OrigLabelFile->ReadLabelFile("$TranslitFileName");
        	$OrigLabelFile->ReadLabelFile("$LabelFileName");
        	# Construct the target labelfile
        	my $TargetLabelFile = new SentenceLabel;
        	$TargetLabelFile->FoldedIDcodes('SENTENCE', 'TRANSLIT', 'WORDS');
        	$TargetLabelFile->ReadLabelFile("$TranslitFileName");
        	$TargetLabelFile->ReadLabelFile("$LabelFileName");
          # Initialize all labelfiles
        	$AspexLabelFile->presetItem('PHONEMES');
        	$OrigLabelFile->presetItem('PHONEMES');
        	$TargetLabelFile->presetItem('PHONEMES');
        	#
        	$OrigLabelFile->firstItem('TRANSLIT');
        	$TargetLabelFile->firstItem('TRANSLIT');
        	$OrigLabelFile->firstItem('WORDS');
        	$TargetLabelFile->firstItem('WORDS');
        	$OrigLabelFile->firstItem('SYLLABLES');
        	$TargetLabelFile->firstItem('SYLLABLES');
        	
        	# First check: Number of Phonemes is approximately equal
        	my $EquivalenceQuotient = 3.0/4.0;
        	if($AspexLabelFile->TierLength('PHONEMES')/$OrigLabelFile->TierLength('PHONEMES') <= $EquivalenceQuotient)
        	{
        			$LabelError .= "Very few labels " . $AspexLabelFile->TierLength('PHONEMES')
        	               		. " vs. ". $OrigLabelFile->TierLength('PHONEMES') . "\t";
        	}
        	elsif($AspexLabelFile->TierLength('PHONEMES')/$OrigLabelFile->TierLength('PHONEMES') >= 1/$EquivalenceQuotient)
        	{
        			$LabelError .= "Very many labels " . $AspexLabelFile->TierLength('PHONEMES')
        	               		. " vs. ". $OrigLabelFile->TierLength('PHONEMES') . "\t";
        	};
        	
        	# Second check: Length of tiers is equal
        	my $DurationMargin = 0.01;  # Suppress rounding errors
        	if($AspexLabelFile->duration('PHONEMES') < $OrigLabelFile->duration('PHONEMES') - $DurationMargin)
        	{
        			$LabelError .= "Tier too short " . $AspexLabelFile->duration('PHONEMES')
        	               		. " < ". $OrigLabelFile->duration('PHONEMES') . "\t";
        	}
        	elsif($AspexLabelFile->duration('PHONEMES') > $OrigLabelFile->duration('PHONEMES') + $DurationMargin)
        	{
        			$LabelError .= "Tier too long " . $AspexLabelFile->duration('PHONEMES')
        	               		. " > ". $OrigLabelFile->duration('PHONEMES') . "\t";
        	};
          # Print errors
          print STDERR "$AspexLabelFileName: $LabelError\n" if $LabelError;
          # Go to next file if this one isn't OK	
        	next if $LabelError;

          # Real work: align phoneme tiers
        	while($AspexLabelFile->nextItem('PHONEMES') > -1)
        	{
        	  $OrigLabelFile->nextItem('PHONEMES');
        	  $TargetLabelFile->nextItem('PHONEMES');
        	  my $NewStart = $AspexLabelFile->currentStart('PHONEMES');
        	  my $NewEnd = $AspexLabelFile->currentEnd('PHONEMES');
        	
        	  # empty labels are silences
        	  $AspexLabelFile->currentValue('PHONEMES', '*') unless $AspexLabelFile->currentValue('PHONEMES') =~ /\S/;
        	
        	  # Phonemes are not identical?
        	  my $PhonemeStatus = $AspexLabelFile->insertedItem('PHONEMES', $TargetLabelFile);
        	  #
        	  # First handle fused phonemes (this requires changing the ORIGINAL labelfile)
        	  if($PhonemeStatus eq 'FUSED' || $PhonemeStatus =~ /FUSED-NONMATCHING\s/)
        	  {
        	      # Get the correct word
        	      $OrigLabelFile->parentItemStart('PHONEMES', 'WORDS');
        	      my $ItemFromBack = $OrigLabelFile->TierLength('WORDS') - $OrigLabelFile->currentItem('WORDS');
        	      $TargetLabelFile->currentItem('WORDS', $TargetLabelFile->TierLength('WORDS')-$ItemFromBack);
                # Determine number of syllables
                $OrigLabelFile->presetItem('SYLLABLES');
                my $FusedValue = $AspexLabelFile->currentValue('PHONEMES');
                if($FusedValue =~ /\-/ && $FusedValue !~ /\_/)
                {
                    my $NumberOfSyllables = 0;
                    while($OrigLabelFile->nextStartininterval('SYLLABLES', 'WORDS') > -1)
                    {
                        ++$NumberOfSyllables;
                    };

                    # A word boundary has been labeled as a syllable boundary
                    if($NumberOfSyllables == 1)
                    {
                        $FusedValue =~ s/^(\s*[^\-]+)[\-]/\1\_/g;
                        $AspexLabelFile->currentValue('PHONEMES', $FusedValue);
                    };
                };
        	
        	      # Fuse words
        	      my @WordBoundaries = grep(/[\_]/, split('', $AspexLabelFile->currentValue('PHONEMES')));
        	      my @WordIDcodes = ( $TargetLabelFile->currentIDcode('WORDS') );
        	      while(@WordBoundaries)
        	      {
        	          my $NewWord = $TargetLabelFile->currentValue('WORDS')
        	                        . shift(@WordBoundaries)
        	                        . $TargetLabelFile->readNextValue('WORDS');
        	          push(@WordIDcodes, $TargetLabelFile->readNextIDcode('WORDS'));
        	
        	          $TargetLabelFile->removeItem('WORDS');
        	          $TargetLabelFile->currentValue('WORDS', $NewWord);
        	          $OrigLabelFile->removeItem('WORDS');
        	          $OrigLabelFile->currentValue('WORDS', $NewWord);
        	      };
        	      # If there are codes to merge
        	      if(scalar(@WordIDcodes) > 1)
        	      {
        	          my $FirstIDcode = $WordIDcodes[0];
        	          $FirstIDcode =~ s/([A-Z])([\d])/\1 \2/ig;
        	          $FirstIDcode =~ s/([\d])([A-Z])/\1 \2/ig;
        	          my @FirstIDlist = split(' ', $FirstIDcode);
        	
        	          my $LastIDcode =  $WordIDcodes[$#WordIDcodes];
        	          $LastIDcode =~ s/([A-Z])([\d])/\1 \2/ig;
        	          $LastIDcode =~ s/([\d])([A-Z])/\1 \2/ig;
        	          my @LastIDlist = split(' ', $LastIDcode);
        	
        	          my $i = 0;
        	          while(@FirstIDlist && @LastIDlist && $FirstIDlist[0] eq $LastIDlist[0])
        	          {
        	              shift(@FirstIDlist);
        	              shift(@LastIDlist);
        	          };
        	          my $MergedIDcode = $WordIDcodes[0] . "+".join("", @LastIDlist);
        	          $TargetLabelFile->currentIDcode('WORDS', $MergedIDcode);
        	          @WordIDcodes = ();

        	      };        	
        	
        	      # Get the correct syllable
        	      $OrigLabelFile->parentItemStart('PHONEMES', 'SYLLABLES');
        	      $ItemFromBack = $OrigLabelFile->TierLength('SYLLABLES') - $OrigLabelFile->currentItem('SYLLABLES');
        	      $TargetLabelFile->currentItem('SYLLABLES', $TargetLabelFile->TierLength('SYLLABLES')-$ItemFromBack);
        	      # Fuse syllables
        	      my @SyllableBoundaries = grep(/[\_\-]/, split('', $AspexLabelFile->currentValue('PHONEMES')));
        	      while(@SyllableBoundaries)
        	      {
        	          my $NewSyllable = $TargetLabelFile->currentValue('SYLLABLES')
        	                        . shift(@SyllableBoundaries)
        	                        . $TargetLabelFile->readNextValue('SYLLABLES');
        	          $TargetLabelFile->removeItem('SYLLABLES');
        	          $TargetLabelFile->currentValue('SYLLABLES', $NewSyllable);
        	          $OrigLabelFile->removeItem('SYLLABLES');
        	          $OrigLabelFile->currentValue('SYLLABLES', $NewSyllable);
        	      };
        	
        	      # Remove all fused phonemes and replace with Aspex value
        	      # Determine the number of phonemes to replace
        	      my @PhonemeBoundaries = grep(/[\_\-\s]/, split('', $AspexLabelFile->currentValue('PHONEMES'))); # Becomes cleaned Aspex value
        	      my $NumberOfPhonemes = scalar(@PhonemeBoundaries);
        	      $NumberOfPhonemes = $' - 1 if $PhonemeStatus =~ /FUSED-NONMATCHING\s*/ && $' > 0;
        	      while($NumberOfPhonemes > 0)
        	      {
        	          my $NewPhoneme = $TargetLabelFile->currentValue('PHONEMES')
        	                        . shift(@PhonemeBoundaries)
        	                        . $TargetLabelFile->readNextValue('PHONEMES');
        	          $TargetLabelFile->removeItem('PHONEMES');
        	          $TargetLabelFile->currentValue('PHONEMES', $NewPhoneme);
        	          $OrigLabelFile->removeItem('PHONEMES');
        	          $OrigLabelFile->currentValue('PHONEMES', $NewPhoneme);
        	          --$NumberOfPhonemes;
        	      };
        	      # Write correct value
        	      $TargetLabelFile->currentValue('PHONEMES', $AspexLabelFile->currentValue('PHONEMES'));
        	      $OrigLabelFile->currentValue('PHONEMES', $AspexLabelFile->currentValue('PHONEMES'));
        	  };
        	  # A deleted phoneme
        	  if($PhonemeStatus =~ /DELETED/)
        	  {
                 $TargetLabelFile->currentValue('SYLLABLES'), " ",
                 $TargetLabelFile->currentValue('WORDS'), " : ",
                 $AspexLabelFile->currentValue('PHONEMES'), "\n";

                 # Adapt words
        	  	   # Deleted phoneme was a complete word (silence?), remove it
        	  	   if($OrigLabelFile->Startisaligned('PHONEMES', 'WORDS') > -1
        	  	   		&& $OrigLabelFile->Endisaligned('PHONEMES', 'WORDS') > -1)
        	  	   {
        	  	       $TargetLabelFile->removeItem('WORDS');
        	           $OrigLabelFile->nextItem('WORDS');
        	  	   }
        	  	   # Deleted phoneme was the start of a word, shift start
        	  	   elsif($OrigLabelFile->Startisaligned('PHONEMES', 'WORDS') > -1)
        	  	   {
        	          $TargetLabelFile->currentStart('WORDS', $NewStart);
        	       }
        	       # Deleted phoneme was the end of a word, shift end of word and step to next
        	  	   elsif($OrigLabelFile->Endisaligned('PHONEMES', 'WORDS') > -1)
        	  	   {
        	          $TargetLabelFile->currentEnd('WORDS', $NewStart);
        	          $TargetLabelFile->nextItem('WORDS');
        	          $TargetLabelFile->currentStart('WORDS', $NewStart);
        	          $OrigLabelFile->nextItem('WORDS');
        	       };
        	
        	  	   # Adapt syllabless
        	  	   # Deleted phoneme was a complete syllable, remove it
        	  	   if($OrigLabelFile->Startisaligned('PHONEMES', 'SYLLABLES') > -1
        	  	   		&& $OrigLabelFile->Endisaligned('PHONEMES', 'SYLLABLES') > -1)
        	  	   {
        	  	       $TargetLabelFile->removeItem('SYLLABLES');
        	          $OrigLabelFile->nextItem('SYLLABLES');
        	  	   }
        	  	   # Deleted phoneme was the start of a syllable, shift start
        	  	   elsif($OrigLabelFile->Startisaligned('PHONEMES', 'SYLLABLES') > -1)
        	  	   {
        	          $TargetLabelFile->currentStart('SYLLABLES', $NewStart);
        	       }
        	       # Deleted phoneme was the end of a syllable, shift end of syllable and step to next
        	  	   elsif($OrigLabelFile->Endisaligned('PHONEMES', 'SYLLABLES') > -1)
        	  	   {
        	          $TargetLabelFile->currentEnd('SYLLABLES', $NewStart);
        	          $TargetLabelFile->nextItem('SYLLABLES');
        	          $TargetLabelFile->currentStart('SYLLABLES', $NewStart);
        	          $OrigLabelFile->nextItem('SYLLABLES');
        	       };
        	
        	       # Remove phoneme in Target and reposition the PHONEMES tier of the original label file
        	  	   $TargetLabelFile->removeItem('PHONEMES') if $TargetLabelFile->currentItem('PHONEMES') > -1;
        	  	   $OrigLabelFile->nextItem('PHONEMES') if $OrigLabelFile->currentItem('PHONEMES') > -1;
        	  	
        	  };
        	  # An inserted phoneme
        	  if($PhonemeStatus =~ /INSERTED/)
        	  {
        	          	  	
        	       # Insert a silence
        	  	   if($AspexLabelFile->currentValue('PHONEMES') =~ /^\s*([\*\#]|sil)\s*$/)
        	  	   {        	  	   		
        	  	   		   if($TargetLabelFile->parentItemEnd('PHONEMES', 'WORDS') > -1)
        	  	   		   {
        	  	              $TargetLabelFile->insertItemBefore('WORDS',
        	  	                                      '#', '',
        	  	                                      $NewStart);
														$OrigLabelFile->previousItem('WORDS');
        	  	         };
        	  	   		   if($TargetLabelFile->parentItemEnd('PHONEMES', 'SYLLABLES') > -1)
        	  	   		   {
        	  	              $TargetLabelFile->insertItemBefore('SYLLABLES',
        	  	                                      '*', '',
        	  	                                      $NewStart);
														$OrigLabelFile->previousItem('SYLLABLES');
        	  	         };   		
        	  	   }
        	  	   # Not a silence: Handle alignment
        	  	   else
        	  	   {
        	  	         my $PreviousPhoneme =  $AspexLabelFile->readPreviousValue('PHONEMES');
        	  	         my $CurrentPhoneme =  $AspexLabelFile->currentValue('PHONEMES');
        	  	         my $NextPhoneme =  $AspexLabelFile->readNextValue('PHONEMES');
        	  	         # Get the sonority of the previous, current and next phoneme
        	  	         my $PreviousSonority =  $Sonority{$PreviousPhoneme};
        	  	         my $CurrentSonority =  $Sonority{$CurrentPhoneme};
        	  	         my $NextSonority =  $Sonority{$NextPhoneme};
        	  	         # Get the phoneme class of the previous, current and next phoneme
        	  	         my $PreviousClass =  $PhonemeClass{$PreviousPhoneme};
        	  	         my $CurrentClass =  $PhonemeClass{$CurrentPhoneme};
        	  	         my $NextClass =  $PhonemeClass{$NextPhoneme};
         	  	         #
         	  	         # Insertion at a Syllable boundary
        	  	   		   if($OrigLabelFile->Startisaligned('PHONEMES', 'SYLLABLES') > -1)
        	  	   		   {
        	  	   		      my $NotSyllableInitial = 0;
        	  	   		      $NotSyllableInitial = $NotInitialRules{"$CurrentClass-$NextClass"};
        	  	   		      $NotSyllableInitial ||= $NotInitialRules{"$CurrentPhoneme-$NextClass"};
        	  	   		      $NotSyllableInitial ||= $NotInitialRules{"$CurrentClass-$NextPhoneme"};
        	  	   		      $NotSyllableInitial ||= $NotInitialRules{"$CurrentPhoneme-$NextPhoneme"};
        	  	   		
        	  	   		      # Not syllable initial (i.e., syllable final)
        	  	   		      if($NotSyllableInitial ||
        	  	   		         ($CurrentSonority >= $NextSonority) ||
        	  	   		         ($CurrentClass eq $NextClass))
        	  	   		      {
        	  	               $TargetLabelFile->previousItem('SYLLABLES');
        	  	               $TargetLabelFile->currentEnd('SYLLABLES', $NewEnd);
        	  	               $TargetLabelFile->nextItem('SYLLABLES');
        	  	               $TargetLabelFile->currentStart('SYLLABLES', $NewEnd);
        	  	
        	  	               # also a word boundary
        	  	               if($OrigLabelFile->Startisaligned('PHONEMES', 'WORDS') > -1)
        	  	               {
        	  	                   $TargetLabelFile->previousItem('WORDS');
        	  	                   $TargetLabelFile->currentEnd('WORDS', $NewEnd);
        	  	                   $TargetLabelFile->nextItem('WORDS');
        	  	                   $TargetLabelFile->currentStart('WORDS', $NewEnd);
        	  	               };
        	  	            }
        	  	            else  # Must be syllable initial
        	  	            {
        	  	               $TargetLabelFile->currentStart('SYLLABLES', $NewStart);
														 $OrigLabelFile->currentStart('SYLLABLES', $NewStart); # Remove alignment
        	  	
        	  	               if($OrigLabelFile->Startisaligned('PHONEMES', 'WORDS') > -1)
        	  	               {
        	  	                   $TargetLabelFile->currentStart('WORDS', $NewStart);
														     $OrigLabelFile->currentStart('WORDS', $NewStart); # Remove alignment
        	  	               };
        	  	            };
        	  	         };   		
        	  	   };
        	  	
        	  	   # Insert the actual new phoneme in the phoneme stream
        	  	   $TargetLabelFile->insertItemBefore('PHONEMES',
        	  	                                      $AspexLabelFile->currentValue('PHONEMES'), '',
        	  	                                      $NewStart);
								 $OrigLabelFile->previousItem('PHONEMES');
        	  };
        	  # A substituted phoneme
        	  if($PhonemeStatus eq 'SUBSTITUTED')
        	  {
        	  	   $TargetLabelFile->currentValue('PHONEMES', $AspexLabelFile->currentValue('PHONEMES'));
        	  };
         	
        	  # The Easy cases: No insertions or deletions
        	  # Adapt STARTS
        	  if($OrigLabelFile->Startisaligned('PHONEMES', 'WORDS') > -1)
        	  {
       	      $TargetLabelFile->currentStart('WORDS', $NewStart);
        	  };
        	  if($OrigLabelFile->Startisaligned('PHONEMES', 'SYLLABLES') > -1)
        	  {
        	      $TargetLabelFile->currentStart('SYLLABLES', $NewStart);
        	  };
        	  $TargetLabelFile->currentStart('PHONEMES', $NewStart);
        	
       	    # Adapt ENDS and step in other tiers

        	  if($OrigLabelFile->Endisaligned('PHONEMES', 'WORDS') > -1)
        	  {
        	      $TargetLabelFile->currentEnd('WORDS', $NewEnd);
        	      $TargetLabelFile->nextItem('WORDS');
        	      $TargetLabelFile->currentStart('WORDS', $NewEnd);
        	      $OrigLabelFile->nextItem('WORDS');
        	  };
        	  if($OrigLabelFile->Endisaligned('PHONEMES', 'SYLLABLES') > -1)
        	  {
        	      $TargetLabelFile->currentEnd('SYLLABLES', $NewEnd);
        	      $TargetLabelFile->nextItem('SYLLABLES');
        	      $TargetLabelFile->currentStart('SYLLABLES', $NewEnd);
        	      $OrigLabelFile->nextItem('SYLLABLES');
        	  };
        	  $TargetLabelFile->currentEnd('PHONEMES', $NewEnd);
      	};

      	# Fill in the actual syllable transcriptions
      	$TargetLabelFile->presetItem('SYLLABLES');
      	while($TargetLabelFile->nextItem('SYLLABLES') > -1)
      	{
              my @StressList = split(/[\-\_]/, $TargetLabelFile->currentValue('SYLLABLES'));
      	      my $SyllableTranscription = "";
      	      $SyllableTranscription .= '"' if shift(@StressList) =~ /\"/;
      	      $TargetLabelFile->presetItem('PHONEMES');
      	      while($TargetLabelFile->nextStartininterval('PHONEMES', 'SYLLABLES') > -1)
      	      {
      	         $SyllableTranscription .= $TargetLabelFile->currentValue('PHONEMES');
      	         # Next syllable
      	         if($TargetLabelFile->currentValue('PHONEMES') =~ /[\-\_]/)
      	         {
      	             # Add a stress marker if necessary
      	             if(shift(@StressList) =~ /\"/)
      	             {
      	                  $SyllableTranscription =~ s/([\-\_])([^\-\_]*$)/\1\"\2/g;
      	             };
      	         };
      	      };
      	
      	      # Remove spaces
      	      $SyllableTranscription =~ s/\s//g;
      	      # Write the new transcription
      	      my $CheckTranscription = length($SyllableTranscription)-length($TargetLabelFile->currentValue('SYLLABLES'));
              if(abs($CheckTranscription) > 3)
              {
              		print STDERR "$FileName: ",
              		$TargetLabelFile->currentValue('SYLLABLES'), " -> $SyllableTranscription \n";
              		last;
              }
              else
              {
      			  		$TargetLabelFile->currentValue('SYLLABLES', $SyllableTranscription);
      			  };
      	};
      	      	
      	# Align sentence and translit tiers to words tier
      	$TargetLabelFile->firstItem('TRANSLIT');     	
      	$TargetLabelFile->firstItem('WORDS');
      	$TargetLabelFile->currentStart('TRANSLIT', $TargetLabelFile->currentStart('WORDS'));
      	$TargetLabelFile->currentEnd('TRANSLIT', $TargetLabelFile->currentEnd('WORDS'));
      	# Start of sentence;     	
      	$TargetLabelFile->nextItem('TRANSLIT');
      	while($TargetLabelFile->nextItem('WORDS') > -1 && $TargetLabelFile->currentValue('WORDS') !~ /\w/){};
      	$TargetLabelFile->currentStart('TRANSLIT', $TargetLabelFile->currentStart('WORDS'));
      	# End of sentence
      	$TargetLabelFile->lastItem('WORDS');
      	while($TargetLabelFile->previousItem('WORDS') > -1 && $TargetLabelFile->currentValue('WORDS') !~ /\w/){};
      	$TargetLabelFile->currentEnd('TRANSLIT', $TargetLabelFile->currentEnd('WORDS'));
      	$TargetLabelFile->nextItem('TRANSLIT');
      	$TargetLabelFile->currentStart('TRANSLIT', $TargetLabelFile->currentEnd('WORDS'));
      	$TargetLabelFile->currentEnd('TRANSLIT', $TargetLabelFile->duration('WORDS'));
      	#
      	# Align sentence tier
      	$TargetLabelFile->presetItem('TRANSLIT');     	
      	$TargetLabelFile->presetItem('SENTENCE');
      	while($TargetLabelFile->nextItem('TRANSLIT') > -1 && $TargetLabelFile->nextItem('SENTENCE') > -1)
      	{
      	    $TargetLabelFile->currentStart('SENTENCE', $TargetLabelFile->currentStart('TRANSLIT'));
      	    $TargetLabelFile->currentEnd('SENTENCE', $TargetLabelFile->currentEnd('TRANSLIT'));
      	};
      	
      	# Update item ID's
      	$TargetLabelFile->InterpolateIDtier('WORDS');
      	
        $TargetLabelFile->print($TargetFileName, 'SENTENCE', 'TRANSLIT', 'WORDS', 'SYLLABLES', 'PHONEMES');

       };
    };
};

1; # Make require happy


