########################################################################### # The library was developed based upon the idea introduced # # by Nivja DeJong and Ton Wempe [1], Paul Boersma and David Weenink [2], # # Carlo Gussenhoven [3], # # S.M Witt and S.J. Young [4] # # Peaks in intensity (dB) that are preceded and followed by dips in # # intensity are considered as potential syllable cores. # # # # Praat Script voice analysis # # Copyright (C) 2017 Shahab Sabahi # # # # This program is a Mysolutions software intellectual property: # # you can redistribute it and/or modify it under the terms # # of the Mysolutions Permision. # # # # 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. # # # # # ########################################################################### # # modified 2018 July by Shahab Sabahi, # bug fixed concerning summing total pause, May 28th 2017 ########################################################################### clearinfo # select all # Remove form Counting Syllables in Sound Utterances real Silence_threshold_(dB) -20 real Minimum_dip_between_peaks_(dB) 2 real Minimum_pause_duration_(s) 0.27 boolean Keep_Soundfiles_and_Textgrids 1 sentence soundin sentence directory positive Minimum_pitch_(Hz) 80 positive Maximum_pitch_(Hz) 400 positive Time_step_(s) 0.01 endform # shorten variables silencedb = 'silence_threshold' mindip = 'minimum_dip_between_peaks' showtext = 'keep_Soundfiles_and_Textgrids' minpause = 'minimum_pause_duration' # read files Read from file... 'soundin$' # use object ID soundname$ = selected$("Sound") soundid = selected("Sound") originaldur = Get total duration # allow non-zero starting time bt = Get starting time # Use intensity to get threshold To Intensity... 50 0 yes intid = selected("Intensity") start = Get time from frame number... 1 nframes = Get number of frames end = Get time from frame number... 'nframes' # estimate noise floor minint = Get minimum... 0 0 Parabolic # estimate noise max maxint = Get maximum... 0 0 Parabolic #get .99 quantile to get maximum (without influence of non-speech sound bursts) max99int = Get quantile... 0 0 0.99 # estimate Intensity threshold threshold = max99int + silencedb threshold2 = maxint - max99int threshold3 = silencedb - threshold2 if threshold < minint threshold = minint endif # get pauses (silences) and speakingtime To TextGrid (silences)... threshold3 minpause 0.1 silent sounding textgridid = selected("TextGrid") silencetierid = Extract tier... 1 silencetableid = Down to TableOfReal... sounding nsounding = Get number of rows npauses = 'nsounding' speakingtot = 0 for ipause from 1 to npauses beginsound = Get value... 'ipause' 1 endsound = Get value... 'ipause' 2 speakingdur = 'endsound' - 'beginsound' speakingtot = 'speakingdur' + 'speakingtot' endfor select 'intid' Down to Matrix matid = selected("Matrix") # Convert intensity to sound To Sound (slice)... 1 sndintid = selected("Sound") # use total duration, not end time, to find out duration of intdur # in order to allow nonzero starting times. intdur = Get total duration intmax = Get maximum... 0 0 Parabolic # estimate peak positions (all peaks) To PointProcess (extrema)... Left yes no Sinc70 ppid = selected("PointProcess") numpeaks = Get number of points # fill array with time points for i from 1 to numpeaks t'i' = Get time from index... 'i' endfor # fill array with intensity values select 'sndintid' peakcount = 0 for i from 1 to numpeaks value = Get value at time... t'i' Cubic if value > threshold peakcount += 1 int'peakcount' = value timepeaks'peakcount' = t'i' endif endfor # fill array with valid peaks: only intensity values if preceding # dip in intensity is greater than mindip select 'intid' validpeakcount = 0 currenttime = timepeaks1 currentint = int1 for p to peakcount-1 following = p + 1 followingtime = timepeaks'following' dip = Get minimum... 'currenttime' 'followingtime' None diffint = abs(currentint - dip) if diffint > mindip validpeakcount += 1 validtime'validpeakcount' = timepeaks'p' endif currenttime = timepeaks'following' currentint = Get value at time... timepeaks'following' Cubic endfor # Look for only voiced parts select 'soundid' To Pitch (ac)... 0.02 30 4 no 0.03 0.25 0.01 0.35 0.25 450 # keep track of id of Pitch pitchid = selected("Pitch") voicedcount = 0 for i from 1 to validpeakcount querytime = validtime'i' select 'textgridid' whichinterval = Get interval at time... 1 'querytime' whichlabel$ = Get label of interval... 1 'whichinterval' select 'pitchid' value = Get value at time... 'querytime' Hertz Linear if value <> undefined if whichlabel$ = "sounding" voicedcount = voicedcount + 1 voicedpeak'voicedcount' = validtime'i' endif endif endfor # calculate time correction due to shift in time for Sound object versus # intensity object timecorrection = originaldur/intdur # Insert voiced peaks in TextGrid if showtext > 0 select 'textgridid' Insert point tier... 1 syllables for i from 1 to voicedcount position = voicedpeak'i' * timecorrection Insert point... 1 position 'i' endfor endif Save as text file: "'directory$'/'soundname$'.TextGrid" # use object ID Read from file... 'soundin$' soundname$ = selected$("Sound") soundid = selected("Sound") fileName$ = "f0points'soundname$'.txt" # Calculate F0 values To Pitch... time_step minimum_pitch maximum_pitch numberOfFrames = Get number of frames # Loop through all frames in the Pitch object: select Pitch 'soundname$' unit$ = "Hertz" min_Hz = Get minimum... 0 0 Hertz Parabolic min$ = "'min_Hz'" max_Hz = Get maximum... 0 0 Hertz Parabolic max$ = "'max_Hz'" mean_Hz = Get mean... 0 0 Hertz mean$ = "'mean_Hz'" stdev_Hz = Get standard deviation... 0 0 Hertz stdev$ = "'stdev_Hz'" median_Hz = Get quantile... 0 0 0.50 Hertz median$ = "'median_Hz'" quantile25_Hz = Get quantile... 0 0 0.25 Hertz quantile25$ = "'quantile25_Hz'" quantile75_Hz = Get quantile... 0 0 0.75 Hertz quantile75$ = "'quantile75_Hz'" # Collect and save the pitch values from the individual frames to the text file: quantile250 = 'quantile25$' quantile750 = 'quantile75$' meanall = 'mean$' sd='stdev$' medi='median$' mini='min$' maxi='max$' # clean up before next sound file is opened select 'intid' plus 'matid' plus 'sndintid' plus 'ppid' plus 'pitchid' plus 'silencetierid' plus 'silencetableid' Read from file... 'soundin$' soundname$ = selected$ ("Sound") To Formant (burg)... 0 5 5500 0.025 50 Read from file... 'directory$'/'soundname$'.TextGrid int=Get number of intervals... 2 appendInfoLine:"int = ", 'int' if int<2 warning$="A noisy background or unnatural-sounding speech detected. No result try again" appendInfoLine: warning$ # exitScript() endif # We then calculate F1, F2 and F3 fff= 0 eee= 0 inside= 0 outside= 0 for k from 2 to 'int' select TextGrid 'soundname$' label$ = Get label of interval... 2 'k' if label$ <> "" # calculates the onset and offset vowel_onset = Get starting point... 2 'k' vowel_offset = Get end point... 2 'k' select Formant 'soundname$' f_one = Get mean... 1 vowel_onset vowel_offset Hertz f_two = Get mean... 2 vowel_onset vowel_offset Hertz f_three = Get mean... 3 vowel_onset vowel_offset Hertz appendInfoLine: "f_one = ", 'f_one' appendInfoLine: "f_two = ", 'f_two' appendInfoLine: "f_three = ", 'f_three' ff = 'f_two'/'f_one' lnf1 = 'f_one' lnf2f1 = ('f_two'/'f_one') uplim =(-0.012*'lnf1')+13.17 lowlim =(-0.0148*'lnf1')+8.18 f1uplim =(lnf2f1-13.17)/-0.012 f1lowlim =(lnf2f1-8.18)/-0.0148 if lnf1>='f1lowlim' and lnf1<='f1uplim' inside = 'inside'+1 else outside = 'outside'+1 endif fff = 'fff'+'f1uplim' eee = 'eee'+'f1lowlim' ffff = 'fff'/'int' eeee = 'eee'/'int' pron =('inside'*100)/('inside'+'outside') prom =('outside'*100)/('inside'+'outside') prob1 = invBinomialP ('pron'/100, 'inside', 'inside'+'outside') prob = 'prob1:2' endif endfor lnf0 = (ln(f_one)-5.65)/0.31 f00 = exp (lnf0) Remove if showtext < 1 select 'soundid' plus 'textgridid' Remove endif # summarize results in Info window speakingrate = 'voicedcount'/'originaldur' speakingraterp = ('voicedcount'/'originaldur')*100/3.93 articulationrate = 'voicedcount'/'speakingtot' articulationraterp = ('voicedcount'/'speakingtot')*100/4.64 npause = 'npauses'-1 asd = 'speakingtot'/'voicedcount' avenumberofwords = ('voicedcount'/1.74)/'speakingtot' avenumberofwordsrp = (('voicedcount'/1.74)/'speakingtot')*100/2.66 nuofwrdsinchunk = (('voicedcount'/1.74)/'speakingtot')* 'speakingtot'/'npauses' nuofwrdsinchunkrp = ((('voicedcount'/1.74)/'speakingtot')* 'speakingtot'/'npauses')*100/9 avepauseduratin = ('originaldur'-'speakingtot')/('npauses'-1) avepauseduratinrp = (('originaldur'-'speakingtot')/('npauses'-1))*100/0.75 balance = ('voicedcount'/'originaldur')/('voicedcount'/'speakingtot') balancerp = (('voicedcount'/'originaldur')/('voicedcount'/'speakingtot'))*100/0.85 nuofwrds= ('voicedcount'/1.74) f1norm = -0.0118*'pron'*'pron'+0.5072*'pron'+394.34 inpro = ('nuofwrds'*60/'originaldur') polish = 'originaldur'/2 # Read the saved pitch points as a Matrix object: if meanall<150 q25='quantile250'/100 q75='quantile750'/140 mr= 'meanall'/119 else q25='quantile250'/183 q75='quantile750'/237 mr= 'meanall'/210 endif # Convert the original minimum and maximum parameters in order to define the x scale of the if q25<=1 and q75<=1 and mr>=0.95 and mr<=1.05 ins=10 elsif q25<=1 and q75<=1 and mr>=0.9 and mr<=1.1 ins=9 elsif q25<=1 and q75<=1 and mr>=0.85 and mr<=1.15 ins=8 elsif mr>=0.9 and mr<=1.1 ins=7 elsif mr>=0.8 and mr<=1.2 ins=6 elsif mr<=0.8 ins=4 else ins=5 endif #SCORING if f00<90 or f00>255 z=1.16 elsif f00<97 or f00>245 z=2 elsif f00<115 or f00>245 z=3 elsif f00<=245 or f00>=115 z=4 else z=1 endif if nuofwrdsinchunk>=6.24 and avepauseduratin<=1.0 l=4 elsif nuofwrdsinchunk>=6.24 and avepauseduratin>1.0 l=3.6 elsif nuofwrdsinchunk>=4.4 and nuofwrdsinchunk<=6.24 and avepauseduratin<=1.15 l=3.3 elsif nuofwrdsinchunk>=4.4 and nuofwrdsinchunk<=6.24 and avepauseduratin>1.15 l=3 elsif nuofwrdsinchunk<4.4 and avepauseduratin<=1.15 l=2 elsif nuofwrdsinchunk<=4.4 and avepauseduratin>1.15 l=1.16 else l=1 endif if balance>=0.69 and avenumberofwords>=2.60 o=4 elsif balance>=0.60 and avenumberofwords>=2.43 o=3.5 elsif balance>=0.5 and avenumberofwords>=2.25 o=3 elsif balance>=0.5 and avenumberofwords>=2.07 o=2 elsif balance>=0.5 and avenumberofwords>=1.95 o=1.16 else o=1 endif if speakingrate<=4.26 and speakingrate>=3.16 q=4 elsif speakingrate<=3.16 and speakingrate>=2.54 q=3.5 elsif speakingrate<=2.54 and speakingrate>=1.91 q=3 elsif speakingrate<=1.91 and speakingrate>=1.28 q=2 elsif speakingrate<=1.28 and speakingrate>=1.0 q=1.16 else q=1 endif if balance>=0.69 and articulationrate>=4.54 w=4 elsif balance>=0.60 and articulationrate>=4.22 w=3.5 elsif balance>=0.50 and articulationrate>=3.91 w=3 elsif balance>=0.5 and articulationrate>=3.59 w=2 elsif balance>=0.5 and articulationrate>=3.10 w=1.16 else w=1 endif if inpro>=119 and ('f1norm'*1.1)>=f1lowlim r = 4 elsif inpro>=119 and ('f1norm'*1.1)=100 and ('f1norm'*1.1)>=f1lowlim r = 3.6 elsif inpro<119 and inpro>=100 and ('f1norm'*1.1)=80 and ('f1norm'*1.1)>=f1lowlim r= 3.2 elsif inpro<100 and inpro>=80 and ('f1norm'*1.1)=70 and ('f1norm'*1.1)>=f1lowlim r = 2.4 elsif inpro<70 and inpro>=60 and ('f1norm'*1.1)>=f1lowlim r = 2 elsif inpro<70 and inpro>=60 and ('f1norm'*1.1)=4.80 and balance>=0.8 qr = 4 elsif articulationrate>=4.80 and balance<0.8 qr = 3.8 elsif articulationrate<4.80 and articulationrate>=4.65 and balance>=0.8 qr = 3.6 elsif articulationrate<4.80 and articulationrate>=4.65 and balance<0.8 qr = 3.4 elsif articulationrate<4.65 and articulationrate>=4.55 and balance>=0.8 qr= 3.2 elsif articulationrate<4.65 and articulationrate>=4.55 and balance<0.8 qr = 2.8 elsif articulationrate<4.55 and articulationrate>=4.40 and balance>=0.8 qr = 2.4 elsif articulationrate<4.40 and articulationrate>=4.30 and balance>=0.8 qr = 2 elsif articulationrate<4.40 and articulationrate>=4.30 and balance<0.8 qr = 1.5 else qr = 1 endif # summarize SCORE in Info window totalscore =(l*2+z*4+o*3+qr*3+w*4+r*4)/20 totalscale= 'totalscore'*25 if totalscore>=3.6 a=4 elsif totalscore>=0.6 and totalscore<2 a=1 elsif totalscore>=2 and totalscore<3 a=2 elsif totalscore>=3 and totalscore<3.6 a=3 else a=0.5 endif if totalscale>=90 s=4 elsif totalscale>=15 and totalscale<50 s=1 elsif totalscale>=50 and totalscale<75 s=2 elsif totalscale>=75 and totalscale<90 s=3 else s=0.5 endif #vvv=a+('totalscale'/100) vvv=totalscore+('totalscale'/100) if vvv>=4 u=4*(1-(randomInteger(1,16)/100)) else u=vvv-(randomInteger(1,16)/100) endif if totalscore>=4 xx=30 elsif totalscore>=3.80 and totalscore<4 xx=29 elsif totalscore>=3.60 and totalscore<3.80 xx=28 elsif totalscore>=3.5 and totalscore<3.6 xx=27 elsif totalscore>=3.3 and totalscore<3.5 xx=26 elsif totalscore>=3.15 and totalscore<3.3 xx=25 elsif totalscore>=3.08 and totalscore<3.15 xx=24 elsif totalscore>=3 and totalscore<3.08 xx=23 elsif totalscore>=2.83 and totalscore<3 xx=22 elsif totalscore>=2.60 and totalscore<2.83 xx=21 elsif totalscore>=2.5 and totalscore<2.60 xx=20 elsif totalscore>=2.30 and totalscore<2.50 xx=19 elsif totalscore>=2.23 and totalscore<2.30 xx=18 elsif totalscore>=2.15 and totalscore<2.23 xx=17 elsif totalscore>=2 and totalscore<2.15 xx=16 elsif totalscore>=1.93 and totalscore<2 xx=15 elsif totalscore>=1.83 and totalscore<1.93 xx=14 elsif totalscore>=1.74 and totalscore<1.83 xx=13 elsif totalscore>=1.66 and totalscore<1.74 xx=12 elsif totalscore>=1.50 and totalscore<1.66 xx=11 elsif totalscore>=1.33 and totalscore<1.50 xx=10 else xx=9 endif overscore = xx*4/30 ov = overscore if xx>=25 xxban$="C" elsif xx>=20 and xx<25 xxban$="B2" elsif xx>=16 and xx<20 xxban$="B1" elsif xx>=10 and xx<16 xxban$="A2" else xxban$="A1" endif qaz = 0.18 rr = (r*4+qr*2+z*1)/7 lu = (l*1+w*2+inpro*4/125)/4 td = (w*1+o*2+inpro*1/125)/3.25 facts=(ln(7/4)*4/7+ln(7/2)*2/7+ln(7)*1/7+ln(4)*1/4+ln(2)*1/2+ln(4)*1/4+ln(3.25)*1/3.25+ln(3.25/2)*2/3.25+ln(3.25/0.25)*0.25/3.25+ln(14.25/7)*7/14.25+ln(14.25/4)*4/14.25+ln(14.25/3.35)*3.25/14.25) totsco = (r*ln(7/4)*4/7+qr*ln(7/2)*2/7+z*ln(7)*1/7+l*ln(4)*1/4+w*ln(2)*1/2+ln(4)*1/4*inpro*4/125+w*ln(3.25)*1/3.25+o*ln(3.25/2)*2/3.25+ln(3.25/0.25)*0.25/3.25*inpro*4/125)/facts if totalscore>=4 totsco=3.9 else totsco=totalscore endif rrr = rr*qaz lulu = lu*qaz tdtd = td*qaz totscoo = totsco*qaz whx=rrr*cos(1.309) why=rrr*sin(1.309) who=4*qaz probpron=(r/4) lstd=(10*l)/4 ostd=(10*o)/4 wstd=(10*w)/4 rstd=(10*r)/4 zstd=(10*z)/4 qstd=(10*qr)/4 Erase all appendInfoLine: "1. voicedcount = ", 'voicedcount:0' appendInfoLine: "2. npause = ", 'npause:0' appendInfoLine: "3. speakingrate = ", 'speakingrate:2' appendInfoLine: "4. articulationrate = ", 'articulationrate:2' appendInfoLine: "5. speakingtot = ", 'speakingtot:2' appendInfoLine: "6. originaldur = ", 'originaldur:2' appendInfoLine: "7. balance = ", 'balance:1' appendInfoLine: "8. meanall = ", 'meanall:2' appendInfoLine: "9. sd = ", 'sd:2' appendInfoLine: "10. medi = ", 'medi:1' appendInfoLine: "11. mini = ", 'mini:0' appendInfoLine: "12. maxi = ", 'maxi:0' appendInfoLine: "13. quantile250 = ", 'quantile250:0' appendInfoLine: "14. quantile750 = ", 'quantile750:0' appendInfoLine: "15. probpron = ", 'probpron:2'