#!/usr/bin/perl
#
# juliusのセグメンテーションキットが出力した 'trans.align' 形式のファイルを、Praatの 'textgrid' 形式に変換します。
# ※ smpPeriod period, fsize sample, fshift sample の値はjuliusのデフォルト設定に準じます。
# ※ 音素は silB で始まり silE で終わっている必要があります。
#
# 実行例（第一引数：入力するファイルのパス　第二引数：出力するファイルのパス）
# > perl transalign2textgrid.pl sample.trans.align sample.Textgrid
#
# 更新履歴
# 2013/04/04 公開
# 2013/08/22 総フレーム数が1000をこえたとき終了時間の計算に失敗していた問題に対処
#
# 宮澤幸希（m-kouki@moegi.waseda.jp）
# 詳細説明は http://shower.human.waseda.ac.jp/~m-kouki/pukiwiki_public/24.html#e7a18792 をご覧ください。

######################################################################

#はじめに音素の総数を調べ、最終音素(silE)の終了時間も計算しておく
$segmentCount = 1;
$segmentSwitch1 = 0;
$segmentSwitch2 = 0;
$finalSegEndframe = 0;
$finalSegEndtime = 0;
#入力ファイル読み込み
open(IN1, $ARGV[0]);
while ($line = <IN1>) { 
	if ($line =~ /begin forced alignment/) {
		$segmentSwitch1 = 1;
	}
	if (($segmentSwitch1 == 1)&&($line =~ /silB/)) {
		$segmentSwitch2 = 1;
	} elsif (($segmentSwitch1 == 1)&&($line =~ /silE/)) {
		$segmentSwitch2 = 0;
		#最終音素(silE)の終了時間の計算
		@str1 = split(/]/, $line);
		@str2 = split(/ /, $str1[0]);
		$finalSegEndframe = $str2[$#str2];	#配列の最後の要素を得る
		#終了時間は「フレームシフトサイズ × (フレーム番号 + 1)」（次の音素の開始時間と合わせるためです）
		$finalSegEndtime = 0.010 * ($finalSegEndframe + 1);
	}
	if ($segmentSwitch2 == 1) {
		$segmentCount++;
	}
}
close(IN1);

#続いて音素を読み込んで、時間情報を計算して、TextGrid形式で出力する
#出力ファイル初期化
open(OUT, "> $ARGV[1]");
print(OUT "File type = \"ooTextFile\"\n");
print(OUT "Object class = \"TextGrid\"\n");
print(OUT "\n");
print(OUT "xmin = 0\n");
print(OUT "xmax = $finalSegEndtime \n");
print(OUT "tiers? <exists>\n");
print(OUT "size = 1\n");
print(OUT "item []: \n");
print(OUT "    item [1]:\n");
print(OUT "        class = \"IntervalTier\" \n");
print(OUT "        name = \"seg\" \n");
print(OUT "        xmin = 0 \n");
print(OUT "        xmax = $finalSegEndtime \n");
print(OUT "        intervals: size = $segmentCount \n");
close(OUT);
#入力ファイル読み込み
$segmentCount = 1;
$segmentSwitch1 = 0;
$segmentSwitch2 = 0;
open(IN2, $ARGV[0]);
while ($line = <IN2>) {
	#print $line;
	if ($line =~ /begin forced alignment/) {
		$segmentSwitch1 = 1;
	}
	if (($segmentSwitch1 == 1)&&($line =~ /silB/)) {
		$segmentSwitch2 = 1;
	}
	if ($segmentSwitch2 == 1) {
		#開始時間・終了時間を取得／計算
		@str1 = split(/]/, $line);
		$str1num = @str1;	#要素数を取得
		if ($str1num >= 2) {	#「]」が存在する行のとき
			print "1:$str1[0]\n";
			@str2 = split(/\[/, $str1[0]);
			$timestring = ltrim($str2[1]);	#先頭の空白文字を削除する
			print "2:$timestring\n";
			@str3 = split(/ /, $timestring);
		}
		$segBeginframe = $str3[0];
		$segEndframe = $str3[$#str3];
		print "3:$segBeginframe,$segEndframe\n";
		
		#開始時間は「フレームシフトサイズ × フレーム番号」
		$segBegintime = 0.010 * $segBeginframe;
		#終了時間は「フレームシフトサイズ × (フレーム番号 + 1)」（次の音素の開始時間と合わせるためです）
		$segEndtime = 0.010 * ($segEndframe + 1);
		print "4:$segBegintime,$segEndtime\n";
		
		#音素名を得る
		@str5 = split(/ /, $str1[1]);
		$segmentName = $str5[$#str5];
		
		chomp($segmentName);	#改行記号を除去
		$segmentName =~ s/\r//;	#改行記号を除去
		$segmentName =~ s/\n//;	#改行記号を除去
		
		#出力ファイル書き込み
		open(OUT, ">> $ARGV[1]");
		print(OUT "        intervals [$segmentCount]:\n");
		print(OUT "            xmin = $segBegintime \n");
		print(OUT "            xmax = $segEndtime \n");
		print(OUT "            text = \"$segmentName\" \n");
		close(OUT);
		$segmentCount++;
	}
	if (($segmentSwitch1 == 1)&&($line =~ /silE/)) {
		$segmentSwitch2 = 0;
	}
}
close(IN2);

# 先頭の空白文字を削除するサブルーチン
sub ltrim {
	my $val = shift;
	$val =~ s/^\s*//;
	return $val;
}
