#邵鹏飞老师制作
#http://www.bubuko.com/infodetail-9283.html

#Praat对声音的标注，通常在研究语料不太多的情况下，完全手工进行，但是如果语料数据很多，这种方式会非常慢，以中英文为例，需要进行音段的详细标注，如果有更好的方式进行自动音段对齐，将会节省很多时间，英文有 SPPAS 这样的工具，但是对于初学者来说，并不容易掌握，在这里如果通过使用脚本的方式，将声音文件和标注文本进行一个对应，这里的对应只能以时长平均值的形式出现，并不是完全意义上的音段对齐，也将节省很多时间，而且对于初学者来说，只是掌握一个脚本的使用就可以。而且这个脚本适用于篇章，有些音段对齐的工作可能对于很长的声音文件并不是很方便，这里的方式，将会解决这个问题。

#首先将长篇章的语料标注出句子序号：


#只保留一层即可。

#其次，制作文本句子对应的序号列表：


#表头要符合上面的名称。所谓syllable一列，主要是考虑到英语需要标注音节层，而在汉语中，这一列可改为汉字的拼音，都要以空格隔开。

#将上述列表制作成文本文件，格式为Unicode格式。

#然后在Praat里运行以下脚本：

########################################################################################################################################################################
#####Designed by Pengfei Shao feipengshao@163.com
########################################################################################################################################################################
#### 以篇章为处理对象，能够做到将内容自动添加进标注中，但是无法强制对齐，只是根据时间平均添加。
#### 要求：
#### 1.有声音文件
#### 2.声音文件有一层标注，标注内容是声音文件的分组，以1，2，3，4，分开，每一个数字代表一句话，或者一个短语
#### 3.制作和上述分组内容对应的文本文件，要求为Unicode格式，文本文件有三列，第一列是和上面对应的序号，表头为order
####   ，第二列是sentence ，如果是中文就是序号代表的汉字句子，如果是英文就是英语句子，第三列是syllable，如果是中文是拼音，如果是英语还是句子
#### 4.汉语会产生至少三层，句子层是汉语句子，单词层是拼音，声韵层是产生的声韵母切分开，英语产生四层，句子层是句子，单词层是单词分开，还有音节层和音段层
########################################################################################################################################################################
#### 2013.9.24 对于汉语拼音，能够对不同类型的音分别标注不同，如zhi标为zh iii等
#### 2013.9.25 filename一列既可以是文件名称，也可以是带扩展名
#### 2013.9.26 添加对于英语的音段的处理
#### 2014.1.12 将这个脚本修改为对于篇章的处理
########################################################################################################################################################################
form information
	comment 请输入句子标注层级：
	positive tier_number 1
	comment 请输入声音文件所在的目录：
	comment (原有的声音文件和TextGrid所在的目录)
	text openpath E:\oldwav
	comment 请输入产生的新Textgrid保存目录：
	text savepath E:\newTextGrid
	comment  
	comment 请输入文本文档所在的路径和文件名称：
	comment (格式是TAB间隔)
	text textPath E:\txt\
	text textFileName en.txt
	
	comment 选择语言种类:
	optionmenu language_of_text: 2
		option English
		option Chinese

endform


if right$(openpath$,1)<>"\"
	openpath$=openpath$+"\"
endif
if right$(savepath$,1)<>"\"
	savepath$=savepath$+"\"
endif
if right$(textPath$,1)<>"\"
	textPath$=textPath$+"\"
endif
textPath$=textPath$+textFileName$


#################################################
# 读内容列表
Read Table from tab-separated file... 'textPath$'
txtSimpleName$=selected$("Table",1)
numOfRows=Get number of rows
for iRows from 1 to numOfRows
	# fileNameOfTxt'iRows'$=Get value... 'iRows' filename
	sentence'iRows'$=Get value... 'iRows' sentence
	order'iRows'$=Get value... 'iRows' order
	syllable'iRows'$=Get value... 'iRows' syllable
endfor
#################################################

Create Strings as file list... fileList 'openpath$'*.wav
numOfWavFiles=Get number of strings
for ifile from 1 to numOfWavFiles
	select Strings fileList
	fileName$=Get string... 'ifile'
	Read from file... 'openpath$''fileName$'
	simpleName$=selected$("Sound",1)
	textGridName$=simpleName$+".TextGrid"
	wavName$=simpleName$+".wav"
	Read from file... 'openpath$''textGridName$'
	
	select TextGrid 'simpleName$'
	Set tier name... 1 sentence
	numberOfIntervals=Get number of intervals... 'tier_number'
	### 将有句子号的一层复制到第二层，并清空
	if language_of_text=1
		Duplicate tier... 1 2 word
	else
		Duplicate tier... 1 2 word
		Duplicate tier... 1 3 PY
	endif
	
	for iIntervals from 1 to numberOfIntervals
		select TextGrid 'simpleName$'
		labelOfIntervalOne$=Get label of interval... 'tier_number' 'iIntervals'

		begin_interval=Get start point... 'tier_number' 'iIntervals'
		end_interval=Get end point... 'tier_number' 'iIntervals'

		if labelOfIntervalOne$<>""
			for iRows from 1 to numOfRows
				orderContent$=order'iRows'$
				if orderContent$=labelOfIntervalOne$
					sentenceDetails$=sentence'iRows'$
					syllableDetails$=syllable'iRows'$
				endif
			endfor
			tempSenDetails$=sentenceDetails$
			tempSylDetails$=syllableDetails$
			if language_of_text=1
				r1=rindex(tempSenDetails$," ")
				len1=length(tempSenDetails$)
				iSen=1
				#### 将空格隔开的单词分开
				repeat
					senStrEach'iSen'$=mid$(tempSenDetails$,r1,len1)
					t1$=senStrEach'iSen'$
					tempSenDetails$=tempSenDetails$-t1$			
					r1=rindex(tempSenDetails$," ")	
					len1=length(tempSenDetails$)
					iSen=iSen+1
				until r1=0
				senStrEach'iSen'$=tempSenDetails$
				totalWordNumber=iSen
				#### 隔开的单词是逆序的，调整顺序，并且删除单词前后的空格
				newMark=1
				for j from 1 to totalWordNumber
					mTemp=totalWordNumber+1-j
					t$=senStrEach'mTemp'$
					if t$<>" "
						temp$=t$
						len=length(temp$)
						repeat
							len=length(temp$)
							leftTemp$=left$(temp$,1)
							if leftTemp$=" "
								temp$=right$(temp$,len-1)
							endif
							rightTemp$=right$(temp$,1)
							if rightTemp$=" "
								temp$=left$(temp$,len-1)
							endif
						until leftTemp$<>" " and rightTemp$<>" "
						newSenStr'newMark'$=temp$
						newMark=newMark+1
					endif
				endfor
				wordNumber2=newMark-1
				wavDuration2=end_interval-begin_interval
				stepTime=wavDuration2/wordNumber2

	
				first_boundary=begin_interval
				last_boundary=end_interval
	
				####添加第二层，单词层边界
				for j from 1 to wordNumber2-1
					bondaryTime=first_boundary+stepTime*j
					Insert boundary... 2 'bondaryTime'
					startBoundaryTwo'j'=first_boundary+stepTime*(j-1)
					endBoundaryTwo'j'=first_boundary+stepTime*j
				endfor
				startBoundaryTwo'wordNumber2'=first_boundary+stepTime*(wordNumber2-1)
				endBoundaryTwo'wordNumber2'=first_boundary+stepTime*wordNumber2

				for j from 1 to wordNumber2
					bondaryTime=first_boundary+stepTime*(j-1)
					newIntervalTemp=Get interval at time... 2 'bondaryTime'
					tempStrToTwo$=newSenStr'j'$
					Set interval text... 2 'newIntervalTemp' 'tempStrToTwo$'
				endfor
				tempTimeForOne=first_boundary+stepTime
				newIntervalTempOne=Get interval at time... 1 'tempTimeForOne'
				Set interval text... 1 'newIntervalTempOne' 'sentenceDetails$'
			####这个else是选择语言类型为英语还是汉语
			else
				
				r1=rindex(tempSylDetails$," ")
				len1=length(tempSylDetails$)
				iSyl=1
				repeat
					sylStrEach'iSyl'$=mid$(tempSylDetails$,r1,len1)
					t1$=sylStrEach'iSyl'$
					tempSylDetails$=tempSylDetails$-t1$			
					r1=rindex(tempSylDetails$," ")
					len1=length(tempSylDetails$)	
					iSyl=iSyl+1
				until r1=0
				sylStrEach'iSyl'$=tempSylDetails$
				totalSyllableNumber=iSyl
	
				newMark=1
				for j from 1 to totalSyllableNumber
					mTemp=totalSyllableNumber+1-j
					t$=sylStrEach'mTemp'$
					if t$<>" "
						temp$=t$
						len=length(temp$)
						repeat
							len=length(temp$)
							leftTemp$=left$(temp$,1)
							if leftTemp$=" "
								temp$=right$(temp$,len-1)
							endif
							rightTemp$=right$(temp$,1)
							if rightTemp$=" "
								temp$=left$(temp$,len-1)
							endif
						until leftTemp$<>" " and rightTemp$<>" "
						newSylStr'newMark'$=temp$
						newMark=newMark+1
					endif
				endfor
	
				syllableNumber3=newMark-1
				wavDuration2=end_interval-begin_interval
				stepTime=wavDuration2/syllableNumber3
				first_boundary=begin_interval
				last_boundary=end_interval
				####添加第二层
				stepTime2=stepTime/2
				for j from 1 to syllableNumber3-1
					bondaryTime=first_boundary+stepTime*j
					Insert boundary... 2 'bondaryTime'
					Insert boundary... 3 'bondaryTime'
					insideBoundaryTime=bondaryTime-stepTime2
					Insert boundary... 3 'insideBoundaryTime'
				endfor
				insideBoundaryTime=last_boundary-stepTime2
				Insert boundary... 3 'insideBoundaryTime'

				for j from 1 to syllableNumber3
					bondaryTime=first_boundary+stepTime*(j-1)
					newIntervalTemp=Get interval at time... 2 'bondaryTime'
					tempStrToTwo$=newSylStr'j'$
					Set interval text... 2 'newIntervalTemp' 'tempStrToTwo$'
				endfor
				tempTimeForOne=first_boundary+stepTime
				newIntervalTempOne=Get interval at time... 1 'tempTimeForOne'
				Set interval text... 1 'newIntervalTempOne' 'sentenceDetails$'
				### 添加第三层音段层！！
				markThree=1
		
				actual_Three'markThree'$="sil"
				for j from 1 to syllableNumber3
					bondaryTime00=first_boundary+stepTime*(j-1)
					tempIntervalNum22=Get interval at time... 2 bondaryTime00
					
					t$=Get label of interval... 2 'tempIntervalNum22'
					lenOfThis=length(t$)
					
					if t$<>"sil"
						bondaryTime=first_boundary+stepTime*j
						beginBoundaryTime=first_boundary+stepTime*(j-1)
						# 以第二层为中心，以时间点为联系，求出第三层的interval序列
						intervalNumThree1=Get interval at time... 3 beginBoundaryTime
						intervalNumThree2=Get interval at time... 3 bondaryTime
						intervalNumThree2=intervalNumThree2-1
						# 主要是考察zh ch sh的问题，所以提取头两个字符和一个字符以此判断
						ctext1$=left$(t$,1)
						ctext2$=left$(t$,2)
				
						# 当是zh ch sh时
						mtext$=mid$(t$,2,lenOfThis-1)
						Set interval text... 3 'intervalNumThree1' 'ctext1$'
						Set interval text... 3 'intervalNumThree2' 'mtext$'
						if ctext2$="zh" or ctext2$="sh" or ctext2$="ch"
							Set interval text... 3 'intervalNumThree1' 'ctext2$'
							mtext$=mid$(t$,3,lenOfThis-2)
							mtext2$=mid$(t$,3,lenOfThis-3)
							tone$=right$(t$,1)
							if mtext2$="i"
								mtext$="iii"+tone$
							endif
							if mtext2$="ui"
								mtext$="uei"+tone$
							endif
							if mtext2$="un"
								mtext$="uen"+tone$
							endif
							if mtext2$="iu"
								mtext$="iou"+tone$
							endif
							if mtext2$="ui"
								mtext$="uei"+tone$
							endif
							Set interval text... 3 'intervalNumThree2' 'mtext$'
						endif
				
						if (ctext1$="z" or ctext1$="s" or ctext1$="c") and ctext2$<>"zh" and ctext2$<>"sh" and ctext2$<>"ch"
							Set interval text... 3 'intervalNumThree1' 'ctext1$'
							mtext$=mid$(t$,2,lenOfThis-1)
							mtext2$=mid$(t$,2,lenOfThis-2)
							tone$=right$(t$,1)
							if mtext2$="i"
								mtext$="ii"+tone$
							endif
							Set interval text... 3 'intervalNumThree2' 'mtext$'
						endif
						if ctext1$="j" or ctext1$="q" or ctext1$="x"
							Set interval text... 3 'intervalNumThree1' 'ctext1$'
							mtext$=mid$(t$,2,lenOfThis-1)
							mtext2$=mid$(t$,2,lenOfThis-2)
							tone$=right$(t$,1)
							if mtext2$="u"
								mtext$="v"+tone$
							endif
							Set interval text... 3 'intervalNumThree2' 'mtext$'
						endif
						if ctext1$="y"
							nextStr$=mid$(t$,2,1)
							if nextStr$="i"
								mtext$=mid$(t$,2,lenOfThis-1)
								Remove right boundary... 3 'intervalNumThree1'
								Set interval text... 3 'intervalNumThree1' 'mtext$'
							else
								mtext2$=mid$(t$,2,lenOfThis-2)
								mtext$=mid$(t$,2,lenOfThis-1)
								mtext$="i"+mtext$
								Remove right boundary... 3 'intervalNumThree1'
								Set interval text... 3 'intervalNumThree1' 'mtext$'
								tone$=right$(t$,1)
					
						
								if mtext2$="u"
									mtext$="v"+tone$
									Set interval text... 3 'intervalNumThree1' 'mtext$'
								endif
							endif
						endif
						if ctext1$="w"
							nextStr$=mid$(t$,2,1)
							if nextStr$="u"
								mtext$=nextStr$
								Remove right boundary... 3 'intervalNumThree1'
								Set interval text... 3 'intervalNumThree1' 'mtext$'
							else
								mtext$=mid$(t$,2,lenOfThis-1)
								mtext$="u"+mtext$
								Remove right boundary... 3 'intervalNumThree1'
								Set interval text... 3 'intervalNumThree1' 'mtext$'
							endif
						endif
					endif
				endfor	
			# 这个是语言选择的结束
			endif
		endif
	endfor
	
	###########################################################
	if language_of_text=1
		Duplicate tier... 2 3 syllable
		Duplicate tier... 3 4 phon
	endif
	###########################################################
	Write to text file... 'savepath$''textGridName$'
	select Sound 'simpleName$'
	Remove
	select TextGrid 'simpleName$'
	Remove
endfor
select Strings fileList
Remove
select Table 'txtSimpleName$'
Remove
exit 脚本运行结束！
