作者:熊子瑜
脚本ID:Praat.XZY20211008.020
上传时间:2021年10月08日
简介:此脚本可基于音高数据文件(allData_PitchTier.txt)自动测算并绘制出单字调音高数据图,结果大致如下所示(作图区域大小由用户提前圈定),并将给出各个点的均值及标准差等数据。注意,此脚本仅适用于单个发音人的音高数据,每次针对一位发音人的数据进行操作。除了可直接使用Hz标度外,还支持 Mel、Erb、Bark、Log、SemiTone、Log z-score 等几种音高换算和规整方式。

#By XIONG Ziyu
#last modify: 2021/09/28
#Praat version: 6.1.05
#功能:根据用户指定的声调音高数据文件进行均值分析并作图,仅适用于单个发音人的音高数据;
#注意:数据文件必须以UTF-8文本编码格式,制表符分隔的数据文件:allData_PitchTier.txt
# 数据文件应含有韵母调类、音高值1、音高值2、……、音高值10、音高段时长等数据列
# 如果前后两个音高点的音高间距大于2.5个半音,则剔除该音节的音高数据。
#
#对话框,接受用户输入参数内容
#PitchTier_DataFileName: 声调音高数据文件:allData_PitchTier.txt
#Color_index_and_list:输入颜色名称字符串,作图时按照输入顺序循环使用线条颜色。
#Diaolei_title:输入调类列数据的列标题。
#Pitch_Point_Number: 音高值的个数。
#Without_following_tones:用英文分号分隔的一些关键字,如果调类中含有某个给定的关键字,则跳过该调类的数据
#Keyword_In_tones:调类中所包含的关键字,如果调类名称中不包含给定的关键字,则跳过该调类的数据。
#addMarker_of_Tones:在各调类名称前添加一个公共的关键字,如“T”。
#Top_Title:顶部标题。
#Hz_to_Pitch:音高换算方式。
#Pitch_Max:输入发音人的音域最大值,单位为Hz,若大于等于99999则自动获取。
#Pitch_Min:输入发音人的音域最小值,单位为Hz,若小于等于-99999则自动获取。
#Duration_Max:输入声调最大时长值,单位为秒,若小于等于0则自动获取。
###########################################################
#以下内容请勿随意修改
###########################################################
form 分析声调音高数据均值并作图
sentence PitchTier_DataFileName(数据文件名) C:\rrr\allData_PitchTier.txt
sentence Diaolei_title(调类列标题) 韵母调类
sentence Without_following_tones(剔除以下调类) ;TX;TY;TZ;
sentence Keyword_In_tones(调类名称关键字)
sentence AddMarker_of_Tones(调类名称前缀) T
sentence Top_Title(图标题) 声调音高数据图
sentence Color_index_and_list(颜色顺序) Red Blue Black Green Grey Yellow Purple Magenta Olive Silver Lime Teal Pink Navy Cyan Maroon
optionmenu Hz_to_Pitch(音高换算方式) 1
option None (不换算)
option Hertz To Mel
option Hertz To Erb
option Hertz To Log
option Hertz To Semitones(ref=100Hz)
option log z-score
option Hertz To Bark
positive Pitch_Point_Number(音高点数) 10
real Data_Isok(标准差范围) 3
boolean clean_all 1
endform
#将Praat读取和存储文本文件的编码格式设定为UTF8,避免汉字乱码
Text reading preferences: "UTF-8"
Text writing preferences: "UTF-8"
#清空主窗口对象列表中的数据对象
Create TextGrid... 0 1 "Mary John bell" bell
select all
Remove
if diaolei_title$=""
msg$="请正确输入调类列数据的列标题!"
call exitMsg
endif
if fileReadable(pitchTier_DataFileName$)=0
msg$="未找到下一指定的数据文件,运行已结束!"+newline$+newline$+pitchTier_DataFileName$
call exitMsg
endif
pitchTier_DataFileName$=replace$(pitchTier_DataFileName$,"/","\",0)
preMarker$=addMarker_of_Tones$
#读入声调音高数据文件
Read Table from tab-separated file: "'pitchTier_DataFileName$'"
Rename: "data"
rNum=Get number of rows
cNum=Get number of columns
for c from 1 to cNum
cName$=Get column label: 'c'
if left$(cName$,3)="音高值" or left$(cName$,3)="音高段"
Set column label (index): 'c', "X'cName$'"
endif
endfor
#获取线条颜色名称
color_index_and_list$=color_index_and_list$+" "
colorOk$=" Red Blue Black Green Grey Yellow Purple Magenta Olive Silver Lime Teal Pink Navy Cyan Maroon "
colorNum=0
while index(color_index_and_list$," ")>0
ltxt$=left$(color_index_and_list$,index(color_index_and_list$," ")-1)
color_index_and_list$=right$(color_index_and_list$,length(color_index_and_list$)-index(color_index_and_list$," "))
if ltxt$ != "" and index(colorOk$," "+ltxt$+" ")>0
colorNum=colorNum+1
color'colorNum'$=ltxt$
endif
endwhile
#获取拟剔除的调类关键字
without_following_tones$=without_following_tones$+";"
without_following_tones$=replace$(without_following_tones$,",",";",0)
without_following_tones$=replace$(without_following_tones$," ",";",0)
noKeyNum=0
while index(without_following_tones$,";")>0
ltxt$=left$(without_following_tones$,index(without_following_tones$,";")-1)
without_following_tones$=right$(without_following_tones$,length(without_following_tones$)-index(without_following_tones$,";"))
if ltxt$ != ""
noKeyNum=noKeyNum+1
noKey'noKeyNum'$=ltxt$
endif
endwhile
#获取拟保留的调类关键字
keyword_In_tones$=keyword_In_tones$+";"
keyword_In_tones$=replace$(keyword_In_tones$,",",";",0)
keyword_In_tones$=replace$(keyword_In_tones$," ",";",0)
okKeyNum=0
while index(keyword_In_tones$,";")>0
ltxt$=left$(keyword_In_tones$,index(keyword_In_tones$,";")-1)
keyword_In_tones$=right$(keyword_In_tones$,length(keyword_In_tones$)-index(keyword_In_tones$,";"))
if ltxt$ != ""
okKeyNum=okKeyNum+1
okKey'okKeyNum'$=ltxt$
endif
endwhile
#检查是否缺少基础数据列
selectObject: "Table data"
call checkDataColume 'diaolei_title$'
call checkDataColume X音高段时长
for i from 1 to pitch_Point_Number
call checkDataColume X音高值'pitch_Point_Number'
endfor
#根据用户给定的调类关键字、拟排除的调类等信息筛选数据
selectObject: "Table data"
rNum=Get number of rows
for r from 1 to rNum
diaoLei$=Get value: 'r', "'diaolei_title$'"
for i from 1 to noKeyNum
if index(diaoLei$,noKey'i'$)>0
diaoLei$=""
i=999
endif
endfor
if okKeyNum>0
isok=0
for i from 1 to okKeyNum
if index(diaoLei$,okKey'i'$)>0
isok=1
i=999
endif
endfor
if isok=0
diaoLei$=""
endif
endif
for i from 1 to pitch_Point_Number
vv$=Get value: 'r', "X音高值'i'"
v=extractNumber(vv$,"")
if v<50 or vv$="" or vv$="?" or v=undefined
diaoLei$=""
i=999
endif
endfor
if diaoLei$="?" or diaoLei$="" or diaoLei$="NULL" or diaoLei$="NONE" or index(diaoLei$,"UNK")>0
diaoLei$="000ERR"
Set string value: 'r', "'diaolei_title$'", "'diaoLei$'"
endif
endfor
diaoNum=0
diaolei_List$=";"
Sort rows: "'diaolei_title$'"
for r from 1 to rNum
diaoLei$=Get value: 'r', "'diaolei_title$'"
if diaoLei$="000ERR"
Remove row: 'r'
r=r-1
rNum=rNum-1
else
if index(diaolei_List$,";"+diaoLei$+";")=0
diaolei_List$=diaolei_List$+diaoLei$+";"
diaoNum=diaoNum+1
diao'diaoNum'$=diaoLei$
endif
endif
endfor
#根据全部音高数据的均值和标准差,剔除偏离N个标准差的数据
maxStdv=data_Isok
if maxStdv>0 and maxStdv<=5
err=1
while err=1
call DelData
endwhile
endif
#音高数据换算
call ModData
selectObject: "Table data"
rNum=Get number of rows
#测算调类均值
if hz_to_Pitch=1
shuju$="'newline$'Hertz 标度'newline$''newline$'"
endif
if hz_to_Pitch=2
shuju$="'newline$'Mel 标度'newline$''newline$'"
endif
if hz_to_Pitch=3
shuju$="'newline$'Erb 标度'newline$''newline$'"
endif
if hz_to_Pitch=4
shuju$="'newline$'对数标度(Log10)'newline$''newline$'"
endif
if hz_to_Pitch=5
shuju$="'newline$'半音标度(ref=100Hz)'newline$''newline$'"
endif
if hz_to_Pitch=6
shuju$="'newline$'Log Z-score 标度'newline$''newline$'"
endif
if hz_to_Pitch=7
shuju$="'newline$'Bark 标度'newline$''newline$'"
endif
shuju$=shuju$+"调类"
for i from 1 to pitch_Point_Number
shuju$=shuju$+tab$+"X音高值'i'"
endfor
shuju$=shuju$+"'tab$'样本数'tab$'X音高段时长(s)"+newline$
maxDur=0
maxPitch=-999999999
minPitch=999999999
call GetValues
pitch_Min=minPitch
pitch_Max=maxPitch
duration_Max=maxDur
beginPause: "设置发音人的音高和时长阈值"
real: "Pitch_Max", pitch_Max
real: "Pitch_Min", pitch_Min
real: "Duration_Max", duration_Max
clicked=endPause: "Cancel","OK", 2, 1
if clicked=1
exitScript:"脚本程序已被终止运行!'newline$'"
endif
maxPitch=pitch_Max
minPitch=pitch_Min
maxDur=duration_Max
maxDur='maxDur:3'
minPitch='minPitch:3'
maxPitch='maxPitch:3'
if hz_to_Pitch=5 or hz_to_Pitch=6 or hz_to_Pitch=7
minPitch=minPitch+100
maxPitch=maxPitch+100
endif
for d from 1 to diaoNum
ltxt$=diao'd'$
if ltxt$ != ""
diaolei$=ltxt$
call DrawTone
diaoDur'd'=dur
endif
endfor
for d from 1 to diaoNum
dur=diaoDur'd'
diao$=diao'd'$
cMin=minPitch
cMax=maxPitch
selectObject: "PitchTier 'diao$'"
if hz_to_Pitch=5 or hz_to_Pitch=6 or hz_to_Pitch=7
Formula: "self+100"
endif
cMin=floor(cMin)
cMax=ceiling(cMax)
To Pitch: 0.005, 'cMin', 'cMax'
Formula: "if x>'dur' then self*0 else self endif"
endfor
clearinfo
if clean_all=1
Erase all
endif
Axes: 0, 1, 0, 1
Font size: 10
Speckle size: 1
usedTone$=""
for d from 1 to diaoNum
color$=color'd'$
Colour: "'color$'"
diao$=diao'd'$
usedTone$=usedTone$+diao$+" "
dur=diaoDur'd'
selectObject: "Pitch 'diao$'"
du=dur-0.01
v=Get value at time: 'du', "Hertz", "Linear"
Speckle: 0, 'maxDur', 'minPitch', 'maxPitch', "no"
du=du+0.04
if du>maxDur
du=maxDur-0.04
endif
Text special: 'du', "Centre", 'v', "Bottom", "Times", 10, "0", "'preMarker$''diao$'"
endfor
Axes: 0, 1, 0, 1
if hz_to_Pitch=1
Text left: "yes", "Pitch (Hz)"
endif
if hz_to_Pitch=2
Text left: "yes", "Pitch (Mel)"
endif
if hz_to_Pitch=3
Text left: "yes", "Pitch (Erb)"
endif
if hz_to_Pitch=4
Text left: "yes", "Pitch (Log10)"
endif
if hz_to_Pitch=5
Text left: "yes", "Pitch (Semitones re 100Hz)"
endif
if hz_to_Pitch=6
Text left: "yes", "Pitch (Log Z-score)"
endif
if hz_to_Pitch=7
Text left: "yes", "Pitch (Bark)"
endif
Text bottom: "yes", "Time (s)"
Axes: 0, 'maxDur', 'minPitch', 'maxPitch'
Colour: "grey"
Line width: 0.5
Draw inner box
if diaoNum<9
for
d from 1 to diaoNum
color$=color'd'$
Colour: "'color$'"
diao$=diao'd'$
ypos=0.98-(d-1)/(diaoNum)*0.4
Axes: 0, 1, 0, 1
Text special: 1.10, "Centre", 'ypos', "Half", "Times", 10, "0", "'preMarker$''diao$' --- "
endfor
endif
Colour: "black"
if hz_to_Pitch=5 or hz_to_Pitch=6 or hz_to_Pitch=7
minPitch=minPitch-100
maxPitch=maxPitch-100
endif
Axes: 0, 'maxDur', 'minPitch', 'maxPitch'
Marks left: 6, "yes", "yes", "yes"
Colour: "grey"
Marks bottom: 6, "yes", "yes", "yes"
Text top: "no", "'top_Title$'"
Copy to clipboard
sfn$=left$(pitchTier_DataFileName$,rindex(pitchTier_DataFileName$,"."))+"PNG"
filedelete 'sfn$'
Save as 300-dpi PNG file: "'sfn$'"
echo 'shuju$''newline$'在当前这些声调的均值数据中,'newline$'音高最大值:'maxPitch:3''newline$'音高最小值:'minPitch:3''newline$'时长最大值(秒):'maxDur:3''newline$'
Create TextGrid: 0, 1, "Mary John bell", "bell"
select all
Remove
msg$="作图过程已经结束!"
exitScript: "'msg$'"+newline$+newline$+"########## 以下信息不用查看 ##########"+newline$
procedure checkDataColume colName$
selectObject: "Table data"
colPos=Get column index: "'colName$'"
if colPos=0
msg$="缺少“'colName$'”数据,请核查!"
call exitMsg
endif
endproc
procedure GetValues
for d from 1 to diaoNum
ltxt$=diao'd'$
if ltxt$ != ""
diaolei$=ltxt$
selectObject: "Table data"
Extract rows where column (text): "'diaolei_title$'", "is equal to", "'diaolei$'"
Rename: "'diaolei$'"
dur=Get mean: "X音高段时长"
if maxDur<dur
maxDur=dur
endif
selectObject: "Table 'diaolei$'"
for i from 1 to pitch_Point_Number
mv=Get mean: "X音高值'i'"
if maxPitch<mv
maxPitch=mv
endif
if minPitch>mv
minPitch=mv
endif
endfor
selectObject: "Table 'diaolei$'"
Remove
endif
endfor
endproc
procedure DrawTone
selectObject: "Table data"
Extract rows where column (text): "'diaolei_title$'", "is equal to", "'diaolei$'"
Rename: "'diaolei$'"
dur=Get mean: "X音高段时长"
stdv=Get standard deviation: "X音高段时长"
Create PitchTier: "'diaolei$'", 0, 'maxDur'
selectObject: "Table 'diaolei$'"
yangbenshu=Get number of rows
shuju$=shuju$+preMarker$+diaolei$
for i from 1 to pitch_Point_Number
selectObject: "Table 'diaolei$'"
mv'd'_'i'=Get mean: "X音高值'i'"
stdv'd'_'i'=Get standard deviation: "X音高值'i'"
selectObject: "PitchTier 'diaolei$'"
t=dur*(i-1)/(pitch_Point_Number-1)
mv=mv'd'_'i'
stdv=stdv'd'_'i'
Add point: 't', 'mv'
shuju$=shuju$+tab$+"'mv:3'/'stdv:3'"
endfor
shuju$=shuju$+tab$+"'yangbenshu:0'"+tab$+"'dur:3'/'stdv:3'"+newline$
endproc
procedure exitMsg
Create TextGrid: 0, 1, "Mary John bell", "bell"
select all
Remove
echo 'msg$'
exitScript: "'msg$'"+newline$+newline$+"########## 以下信息不用查看 ##########"+newline$
endproc
procedure DelData
err=0
selectObject: "Table data"
rNum=Get number of rows
k=0
vMean=0
for r from 1 to rNum
for i from 1 to pitch_Point_Number
v=Get value: 'r', "X音高值'i'"
vMean=vMean+v
k=k+1
endfor
endfor
vMean=vMean/k
k=0
vStdv=0
for r from 1 to rNum
for i from 1 to pitch_Point_Number
v=Get value: 'r', "X音高值'i'"
vStdv=vStdv+(v-vMean)^2
k=k+1
endfor
endfor
vStdv=(vStdv/(k-1))^0.5
for r from 1 to rNum
del=0
for i from 1 to pitch_Point_Number
v=Get value: 'r', "X音高值'i'"
if abs((v-vMean)/vStdv)>maxStdv
del=1
i=999
endif
endfor
if del=1
diaoLei$="000ERR"
Set string value: 'r', "'diaolei_title$'", "'diaoLei$'"
endif
endfor
diaoNum=0
diaolei_List$=";"
Sort rows: "'diaolei_title$'"
for r from 1 to rNum
diaoLei$=Get value: 'r', "'diaolei_title$'"
if diaoLei$="000ERR"
Remove row: 'r'
r=r-1
rNum=rNum-1
err=1
else
if index(diaolei_List$,";"+diaoLei$+";")=0
diaolei_List$=diaolei_List$+diaoLei$+";"
diaoNum=diaoNum+1
diao'diaoNum'$=diaoLei$
endif
endif
endfor
rNum=Get number of rows
endproc
procedure ModData
selectObject: "Table data"
for i from 1 to pitch_Point_Number
if hz_to_Pitch=2
Formula: "X音高值'i'", "hertzToMel(self)"
endif
if hz_to_Pitch=3
Formula: "X音高值'i'", "hertzToErb(self)"
endif
if hz_to_Pitch=4
Formula: "X音高值'i'", "log10(self)"
endif
if hz_to_Pitch=5
Formula: "X音高值'i'", "hertzToSemitones(self)"
endif
if hz_to_Pitch=6
Formula: "X音高值'i'", "log10(self)"
endif
if hz_to_Pitch=7
Formula: "X音高值'i'", "hertzToBark(self)"
endif
endfor
if hz_to_Pitch=6
selectObject: "Table data"
rNum=Get number of rows
k=0
vMean=0
for r from 1 to rNum
for i from 1 to pitch_Point_Number
v=Get value: 'r', "X音高值'i'"
vMean=vMean+v
k=k+1
endfor
endfor
vMean=vMean/k
k=0
vStdv=0
for r from 1 to rNum
for i from 1 to pitch_Point_Number
v=Get value: 'r', "X音高值'i'"
vStdv=vStdv+(v-vMean)^2
k=k+1
endfor
endfor
vStdv=(vStdv/(k-1))^0.5
for i from 1 to pitch_Point_Number
Formula: "X音高值'i'", "(self-'vMean')/'vStdv'"
endfor
endif
endproc