Praat Script Syllable Nuclei v2

Note that the new (and slightly better) version of this script (v3), including options to measure filled pauses, can be found here:

https://sites.google.com/view/uhm-o-meter

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

# #

# Praat Script Syllable Nuclei #

# Copyright (C) 2008 Nivja de Jong and Ton Wempe #

# #

# 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 3 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, see http://www.gnu.org/licenses/ #

# #

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

#

# modified 2010.09.17 by Hugo Quené, Ingrid Persoon, & Nivja de Jong

# Overview of changes:

# + change threshold-calculator: rather than using median, use the almost maximum

# minus 25dB. (25 dB is in line with the standard setting to detect silence

# in the "To TextGrid (silences)" function.

# Almost maximum (.99 quantile) is used rather than maximum to avoid using

# irrelevant non-speech sound-bursts.

# + add silence-information to calculate articulation rate and ASD (average syllable

# duration.

# NB: speech rate = number of syllables / total time

# articulation rate = number of syllables / phonation time

# + remove max number of syllable nuclei

# + refer to objects by unique identifier, not by name

# + keep track of all created intermediate objects, select these explicitly,

# then Remove

# + provide summary output in Info window

# + do not save TextGrid-file but leave it in Object-window for inspection

# (if requested in startup-form)

# + allow Sound to have starting time different from zero

# for Sound objects created with Extract (preserve times)

# + programming of checking loop for mindip adjusted

# in the orig version, precedingtime was not modified if the peak was rejected !!

# var precedingtime and precedingint renamed to currenttime and currentint

#

# + bug fixed concerning summing total pause, feb 28th 2011

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

# counts syllables of all sound utterances in a directory

# NB unstressed syllables are sometimes overlooked

# NB filter sounds that are quite noisy beforehand

# NB use Silence threshold (dB) = -25 (or -20?)

# NB use Minimum dip between peaks (dB) = between 2-4 (you can first try;

# For clean and filtered: 4)

form Counting Syllables in Sound Utterances

real Silence_threshold_(dB) -25

real Minimum_dip_between_peaks_(dB) 2

real Minimum_pause_duration_(s) 0.3

boolean Keep_Soundfiles_and_Textgrids yes

sentence directory /directory

endform

# shorten variables

silencedb = 'silence_threshold'

mindip = 'minimum_dip_between_peaks'

showtext = 'keep_Soundfiles_and_Textgrids'

minpause = 'minimum_pause_duration'

# print a single header line with column names and units

printline soundname, nsyll, npause, dur (s), phonationtime (s), speechrate (nsyll/dur), articulation rate (nsyll / phonationtime), ASD (speakingtime/nsyll)

# read files

Create Strings as file list... list 'directory$'/*.wav

numberOfFiles = Get number of strings

for ifile to numberOfFiles

select Strings list

fileName$ = Get string... ifile

Read from file... 'directory$'/'fileName$'

# 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

# clean up before next sound file is opened

select 'intid'

plus 'matid'

plus 'sndintid'

plus 'ppid'

plus 'pitchid'

plus 'silencetierid'

plus 'silencetableid'

Remove

if showtext < 1

select 'soundid'

plus 'textgridid'

Remove

endif

# summarize results in Info window

speakingrate = 'voicedcount'/'originaldur'

articulationrate = 'voicedcount'/'speakingtot'

npause = 'npauses'-1

asd = 'speakingtot'/'voicedcount'

printline 'soundname$', 'voicedcount', 'npause', 'originaldur:2', 'speakingtot:2', 'speakingrate:2', 'articulationrate:2', 'asd:3'

endfor