pelican-nlp 0.3.8__py3-none-any.whl → 0.3.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pelican_nlp/_version.py +1 -1
- pelican_nlp/praat/eps_conv.praat +193 -0
- pelican_nlp/praat/polytonia.praat +773 -0
- pelican_nlp/praat/prosogram.praat +102 -0
- pelican_nlp/praat/prosomain.praat +3787 -0
- pelican_nlp/praat/prosoplot.praat +1546 -0
- pelican_nlp/praat/segment.praat +1224 -0
- pelican_nlp/praat/setup.praat +1 -0
- pelican_nlp/praat/stylize.praat +2766 -0
- pelican_nlp/praat/util.praat +632 -0
- {pelican_nlp-0.3.8.dist-info → pelican_nlp-0.3.9.dist-info}/METADATA +1 -1
- {pelican_nlp-0.3.8.dist-info → pelican_nlp-0.3.9.dist-info}/RECORD +16 -7
- {pelican_nlp-0.3.8.dist-info → pelican_nlp-0.3.9.dist-info}/WHEEL +0 -0
- {pelican_nlp-0.3.8.dist-info → pelican_nlp-0.3.9.dist-info}/entry_points.txt +0 -0
- {pelican_nlp-0.3.8.dist-info → pelican_nlp-0.3.9.dist-info}/licenses/LICENSE +0 -0
- {pelican_nlp-0.3.8.dist-info → pelican_nlp-0.3.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1224 @@
|
|
1
|
+
# segment.praat ---
|
2
|
+
# This file is included (indirectly) by prosogram.praat. It isn't a stand-alone script. Use prosogram.praat instead.
|
3
|
+
# Author: Piet Mertens
|
4
|
+
|
5
|
+
# Last modification: 2021-08-03
|
6
|
+
# 2018-03-31 added segmentation method "specsim"
|
7
|
+
# 2018-04-01 added segmentation method "pitchchange"
|
8
|
+
# 2018-08-09 added segmentation method "pitchterrace"
|
9
|
+
# 2019-01-29 debugged voiced_portions
|
10
|
+
# 2019-02-12 changed to new Praat syntax
|
11
|
+
# 2019-02-12 handle double length diacritic in @is_vowel
|
12
|
+
# 2019-12-04 changed @get_boundary
|
13
|
+
# 2021-08-03 added some vowels in @is_vowel
|
14
|
+
|
15
|
+
|
16
|
+
# Procedure hierarchy
|
17
|
+
# make_segmentation
|
18
|
+
# local_peaks_duo (**)
|
19
|
+
# pseudo_syllables (**) for segmentation mode "asyll"
|
20
|
+
# local_peaks_vowels (*) for segmentation mode "vow-nucl"
|
21
|
+
# local_peaks_syllables (*) for segmentation mode "syll"
|
22
|
+
# local_peaks_syllables_vowels (*)for segmentation mode "syll+vow"
|
23
|
+
# local_peaks_rhyme (*) for segmentation mode "rhyme"
|
24
|
+
# local_peaks_loudness (*) for segmentation mode "loudness"
|
25
|
+
#
|
26
|
+
# convexhull
|
27
|
+
# get_local_peak (*)
|
28
|
+
# is_unvoiced_region
|
29
|
+
# get_boundary
|
30
|
+
# voiced_intersection
|
31
|
+
# add_boundary
|
32
|
+
# set_boundary_label
|
33
|
+
# get_peak_duo (**)
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
procedure make_segmentation: segm_method, .at1, .at2, destID, mindiff
|
38
|
+
# Make a segmentation based on a parameter (which can be intensity, loudness, etc, stored in Intensity object),
|
39
|
+
# in time range <.at1> to <.at2>.
|
40
|
+
# The resulting interval tier is written in textgrid <destID>, created before calling this procedure.
|
41
|
+
# mindiff intensity difference threshold for local dips in convex hull (default = 3)
|
42
|
+
@debug_msg: "make_segmentation: entry"
|
43
|
+
|
44
|
+
mindur_nucl = 0.025 ; minimum duration of nucleus (otherwise rejected)
|
45
|
+
if (segm_method == segm_vnucl)
|
46
|
+
@local_peaks_vowels: destID, intensityID, .at1, .at2
|
47
|
+
elsif (segm_method == segm_aloudness)
|
48
|
+
@convexhull: destID, dip_tier, loudnessID, .at1, .at2, mindiff
|
49
|
+
@tier_point_to_interval: destID, dip_tier, syllable_tier, .at1, .at2
|
50
|
+
@local_peaks_loudness: destID, loudnessID, .at1, .at2
|
51
|
+
elsif (segm_method == segm_anucl)
|
52
|
+
@convexhull: destID, dip_tier, intbpID, .at1, .at2, mindiff
|
53
|
+
@tier_point_to_interval: destID, dip_tier, syllable_tier, .at1, .at2
|
54
|
+
@local_peaks_duo: destID, intensityID, intbpID, .at1, .at2
|
55
|
+
elsif (segm_method == segm_asyll)
|
56
|
+
@pseudo_syllables: destID, intensityID, intbpID, .at1, .at2, mindiff
|
57
|
+
elsif (segm_method == segm_msyllvow)
|
58
|
+
@local_peaks_syllables_vowels: destID, intensityID, .at1, .at2
|
59
|
+
elsif (segm_method == segm_msyllpeak)
|
60
|
+
@local_peaks_syllables: destID, intensityID, .at1, .at2
|
61
|
+
elsif (segm_method == segm_mrhyme)
|
62
|
+
@local_peaks_rhyme: destID, intensityID, .at1, .at2
|
63
|
+
elsif (segm_method == segm_voiced)
|
64
|
+
@voiced_portions: destID, intensityID, .at1, .at2
|
65
|
+
elsif (segm_method == segm_specsim)
|
66
|
+
if (not signal_available)
|
67
|
+
@read_signal
|
68
|
+
endif
|
69
|
+
if (not intensity_available)
|
70
|
+
.intensityID = To Intensity: 100, time_step
|
71
|
+
endif
|
72
|
+
@spectral_similarity: soundID, destID, intensityID, .at1, .at2, 0.02
|
73
|
+
removeObject: soundID
|
74
|
+
signal_available = 0
|
75
|
+
elsif (segm_method == segm_pitchchange)
|
76
|
+
@segmentation_pitch: pitchID, pc_threshold, intensityID, intbpID, .at1, .at2, destID, mindiff
|
77
|
+
elsif (segm_method == segm_pitchterrace)
|
78
|
+
@segmentation_pitch2: pitchID, pc_threshold, intensityID, .at1, .at2, mindur_terrace, destID
|
79
|
+
;selectObject: destID
|
80
|
+
;Copy: "Copy nucleiID " + fname$
|
81
|
+
endif
|
82
|
+
@debug_msg: "make_segmentation: exit"
|
83
|
+
endproc
|
84
|
+
|
85
|
+
|
86
|
+
procedure local_peaks_loudness: .grid, .paramID, .t1, .t2
|
87
|
+
# Find nucleus as local peak in loudness function
|
88
|
+
# <.grid> ID of destination textgrid where nuclei will be stored
|
89
|
+
# <.paramID> parameter for which local peak is to be found
|
90
|
+
# <.t1>..<.t2> time range of analysis
|
91
|
+
selectObject: grid
|
92
|
+
.t = .t1
|
93
|
+
while (.t < .t2)
|
94
|
+
.j = Get interval at time.: syllable_tier, .t
|
95
|
+
.x1 = Get start time of interval: syllable_tier, .j
|
96
|
+
.x2 = Get end time of interval: syllable_tier, .j
|
97
|
+
@get_local_peak: .grid, .paramID, .x1, .x2, 1, .x1, .x2
|
98
|
+
.t = .x2
|
99
|
+
endwhile
|
100
|
+
endproc
|
101
|
+
|
102
|
+
|
103
|
+
procedure local_peaks_vowels: .grid, .paramID, .t1, .t2
|
104
|
+
# Find nucleus as local peak in intensity of vowel
|
105
|
+
# <.grid> ID of destination textgrid where nuclei will be stored
|
106
|
+
# <.paramID> parameter for which local peak is to be found
|
107
|
+
# <.t1>..<.t2> time range of analysis
|
108
|
+
@intervals_from_time_range: .grid, phone_tier, .t1, .t2, "first_interval", "last_interval"
|
109
|
+
selectObject: .grid
|
110
|
+
for .j from first_interval to last_interval
|
111
|
+
.x1 = Get start time of interval: phone_tier, .j
|
112
|
+
.x2 = Get end time of interval: phone_tier, .j
|
113
|
+
.label$ = Get label of interval: phone_tier, .j
|
114
|
+
@is_vowel: .label$
|
115
|
+
if (is_vowel)
|
116
|
+
@get_local_peak: .grid, .paramID, .x1, .x2, 1, .x1, .x2
|
117
|
+
endif
|
118
|
+
endfor
|
119
|
+
endproc
|
120
|
+
|
121
|
+
|
122
|
+
procedure local_peaks_syllables: .grid, .paramID, .t1, .t2
|
123
|
+
# Find nucleus as local peak in intensity of syllable
|
124
|
+
# <.grid> ID of destination textgrid where nuclei will be stored
|
125
|
+
# <.paramID> parameter for which local peak is to be found
|
126
|
+
# <.t1>..<.t2> time range of analysis
|
127
|
+
@intervals_from_time_range: .grid, syllable_tier, .t1, .t2, "first_interval", "last_interval"
|
128
|
+
selectObject: .grid
|
129
|
+
for .j from first_interval to last_interval
|
130
|
+
.x1 = Get start time of interval: syllable_tier, .j
|
131
|
+
.x2 = Get end time of interval: syllable_tier, .j
|
132
|
+
.label$ = Get label of interval: syllable_tier, .j
|
133
|
+
.label$ = replace_regex$ (.label$, "^ *", "", 1)
|
134
|
+
.label$ = replace_regex$ (.label$, " *$", "", 1)
|
135
|
+
if (.label$ <> "PAUSE" and .label$ <> "_")
|
136
|
+
@get_local_peak: .grid, .paramID, .x1, .x2, 1, .x1, .x2
|
137
|
+
endif
|
138
|
+
endfor
|
139
|
+
endproc
|
140
|
+
|
141
|
+
|
142
|
+
procedure local_peaks_syllables_vowels: .grid .paramID, .t1, .t2
|
143
|
+
# Find nucleus as local peak in intensity of syllable, starting from local peak in vowel
|
144
|
+
# <.grid> ID of destination textgrid where nuclei will be stored
|
145
|
+
# <.paramID> parameter for which local peak is to be found
|
146
|
+
# <.t1>..<.t2> time range of analysis
|
147
|
+
@intervals_from_time_range: .grid, syllable_tier, .t1, .t2, "first_interval", "last_interval"
|
148
|
+
selectObject: .grid
|
149
|
+
for .j from first_interval to last_interval
|
150
|
+
.x1 = Get start time of interval: syllable_tier, .j
|
151
|
+
.x2 = Get end time of interval: syllable_tier, .j
|
152
|
+
.syll$ = Get label of interval: syllable_tier, .j
|
153
|
+
; @intervals_from_time_range: .grid, phone_tier, .x1, .x2-0.00001, "ph1", "ph2"
|
154
|
+
@intervals_from_time_range_excl: .grid, phone_tier, .x1, .x2, "ph1", "ph2"
|
155
|
+
nrof_vowels = 0
|
156
|
+
nrof_syllabics = 0
|
157
|
+
for phon from ph1 to ph2
|
158
|
+
.label$ = Get label of interval: phone_tier, phon
|
159
|
+
.phon_x1 = Get start time of interval: phone_tier, phon
|
160
|
+
.phon_x2 = Get end time of interval: phone_tier, phon
|
161
|
+
@is_syllabic: .label$
|
162
|
+
if (result)
|
163
|
+
nrof_syllabics += 1
|
164
|
+
if (is_vowel)
|
165
|
+
nrof_vowels += 1
|
166
|
+
endif
|
167
|
+
if (nrof_syllabics == 1)
|
168
|
+
@get_local_peak: .grid, .paramID, .x1, .x2, 1, .phon_x1, .phon_x2
|
169
|
+
else
|
170
|
+
@msg: "Warning: Multiple vowels (or syllabics) in syllable <'.syll$'> at time '.phon_x1:3'"
|
171
|
+
endif
|
172
|
+
endif
|
173
|
+
endfor
|
174
|
+
endfor
|
175
|
+
endproc
|
176
|
+
|
177
|
+
|
178
|
+
procedure local_peaks_rhyme: .grid, .paramID, .t1, .t2
|
179
|
+
# Find nucleus as local peak in intensity of rhyme, starting from local intensity peak in vowel
|
180
|
+
# <.grid> ID of destination textgrid where nuclei will be stored
|
181
|
+
# <.paramID> parameter for which local peak is to be found
|
182
|
+
# <.t1>..<.t2> time range of analysis
|
183
|
+
@debug_msg: "local_peaks_rhyme: entry ('.t1:4'-'.t2:4')"
|
184
|
+
@intervals_from_time_range: .grid, syllable_tier, .t1, .t2, "first_interval", "last_interval"
|
185
|
+
selectObject: .grid
|
186
|
+
for .j from first_interval to last_interval
|
187
|
+
syll_x1 = Get start time of interval: syllable_tier, .j
|
188
|
+
syll_x2 = Get end time of interval: syllable_tier, .j
|
189
|
+
syll_label$ = Get label of interval: syllable_tier, .j
|
190
|
+
; @intervals_from_time_range: .grid, phone_tier, syll_x1, syll_x2-0.00001, "ph1", "ph2"
|
191
|
+
@intervals_from_time_range_excl: .grid, phone_tier, syll_x1, syll_x2, "ph1", "ph2"
|
192
|
+
nrof_vowels = 0
|
193
|
+
nrof_syllabics = 0
|
194
|
+
for phon from ph1 to ph2
|
195
|
+
phon_x1 = Get start time of interval: phone_tier, phon
|
196
|
+
phon_x2 = Get end time of interval: phone_tier, phon
|
197
|
+
label$ = Get label of interval: phone_tier, phon
|
198
|
+
if (phon == ph1 and phon_x1 <> syll_x1)
|
199
|
+
@msg: "Syllable ('syll_label$') starting at 'syll_x1:4' not aligned with phoneme ('label$') start ('phon_x1:4')"
|
200
|
+
endif
|
201
|
+
if (phon == ph2 and phon_x2 <> syll_x2)
|
202
|
+
@msg: "Syllable ('syll_label$') ending at 'syll_x2:4' not aligned with phoneme ('label$') end ('phon_x2:4')"
|
203
|
+
endif
|
204
|
+
@is_syllabic: label$
|
205
|
+
if (result)
|
206
|
+
nrof_syllabics += 1
|
207
|
+
if (is_vowel)
|
208
|
+
nrof_vowels += 1
|
209
|
+
endif
|
210
|
+
if (nrof_syllabics == 1)
|
211
|
+
@get_local_peak: .grid, .paramID, phon_x1, syll_x2, 1, phon_x1, phon_x2
|
212
|
+
else
|
213
|
+
@msg: "Warning: Multiple vowels (or syllabics) in syllable at time 'phon_x1:3'"
|
214
|
+
endif
|
215
|
+
endif
|
216
|
+
endfor
|
217
|
+
endfor
|
218
|
+
@debug_msg: "local_peaks_rhyme: exit"
|
219
|
+
endproc
|
220
|
+
|
221
|
+
|
222
|
+
procedure local_peaks_duo: .dstID, .paramID, .ifilID, .t1, .t2
|
223
|
+
# <.dstID> ID of destination textgrid where nuclei will be stored
|
224
|
+
# <.paramID> parameter for which local peak is to be found (intensity)
|
225
|
+
# <.ifilID> intensity of bandpass filtered signal
|
226
|
+
# <.t1>..<.t2> time range of analysis
|
227
|
+
#
|
228
|
+
# Find syllabic nucleus within syllable-like interval
|
229
|
+
# - For a voiced portion...
|
230
|
+
# - Evaluate importance of difference between maximum of global intensity
|
231
|
+
# and dip at right end of segment. This affects right side of nucleus.
|
232
|
+
# - From intensity peak (of filtered), go left/right until max difference reached.
|
233
|
+
@debug_msg: "local_peaks_duo: entry"
|
234
|
+
|
235
|
+
.time = .t1
|
236
|
+
while (.time < .t2)
|
237
|
+
selectObject: .dstID
|
238
|
+
.j = Get interval at time: syllable_tier, .time
|
239
|
+
.x1 = Get start point: syllable_tier, .j
|
240
|
+
.x2 = Get end point: syllable_tier, .j
|
241
|
+
@is_unvoiced_region: dstID, .x1, .x2
|
242
|
+
# default values used in case of error
|
243
|
+
left = .x1
|
244
|
+
right = .x2
|
245
|
+
if (result = 1) ; fully unvoiced
|
246
|
+
@set_boundary_label: .dstID, syllable_tier, .x1, .x2, "U"
|
247
|
+
else ; fully or partly voiced
|
248
|
+
@get_peak_duo: .x1, .x2
|
249
|
+
; get_peak_duo sets variables: left, right, valid
|
250
|
+
@add_boundary: .dstID, nucleus_tier, left
|
251
|
+
@add_boundary: .dstID, nucleus_tier, right
|
252
|
+
if (valid) ; variable set by get_peak_duo
|
253
|
+
@add_boundary: .dstID, syllable_tier, left
|
254
|
+
@add_boundary: .dstID, syllable_tier, right
|
255
|
+
if (left-.x1 > time_step)
|
256
|
+
@set_boundary_label: .dstID, syllable_tier, .x1, left, "<"
|
257
|
+
endif
|
258
|
+
if (.x2-right > time_step)
|
259
|
+
@set_boundary_label: .dstID, syllable_tier, right, .x2, ">"
|
260
|
+
endif
|
261
|
+
@set_boundary_label: .dstID, nucleus_tier, left, right, "a"
|
262
|
+
@set_boundary_label: .dstID, syllable_tier, left, right, "a"
|
263
|
+
else ; invalid nucleus (too short)
|
264
|
+
@set_boundary_label: .dstID, nucleus_tier, left, right, "reject"
|
265
|
+
@set_boundary_label: .dstID, syllable_tier, .x1, .x2, "<>"
|
266
|
+
endif ; valid
|
267
|
+
endif ; voiced
|
268
|
+
.time = .x2
|
269
|
+
endwhile
|
270
|
+
@debug_msg: "local_peaks_duo: exit"
|
271
|
+
endproc
|
272
|
+
|
273
|
+
|
274
|
+
procedure get_peak_duo: .x1p, .x2p
|
275
|
+
; Find syllabic nucleus boundaries using full-band and BP-filtered intensity
|
276
|
+
; Returns values in <valid>, <left>, <right>
|
277
|
+
valid = 0
|
278
|
+
selectObject: intbpID
|
279
|
+
.tmaxfil = Get time of maximum: .x1p, .x2p, "Parabolic"
|
280
|
+
selectObject: intensityID ; intensity full bandwidth
|
281
|
+
.tmax = Get time of maximum: .x1p, .x2p, "Parabolic"
|
282
|
+
.max = Get maximum: .x1p, .x2p, "Parabolic"
|
283
|
+
if (.max == undefined) ; can happen at end of signal, where intensity is undefined
|
284
|
+
@msg: "get_peak_duo: max undefined at time x1='.x1p:3', x2='.x2p:3'"
|
285
|
+
else
|
286
|
+
repeat ; can happen at end of signal, where intensity is undefined
|
287
|
+
selectObject: intensityID
|
288
|
+
.dip_int = Get value at time: .x2p, "Nearest"
|
289
|
+
selectObject: intbpID
|
290
|
+
.dip_intbp = Get value at time: .x2p, "Nearest"
|
291
|
+
if (.dip_int == undefined or .dip_intbp == undefined)
|
292
|
+
@debug_msg: "get_peak_duo: dip undefined at time '.x2p:3'"
|
293
|
+
.x2p -= time_step
|
294
|
+
endif
|
295
|
+
until (.dip_int <> undefined and .dip_intbp <> undefined)
|
296
|
+
; <diff_left> is initialized in @initialization_multiple_files (prosomain.praat)
|
297
|
+
@get_boundary: intbpID, .tmaxfil, .x1p, -1, diff_left
|
298
|
+
.left_filt = result
|
299
|
+
@get_boundary: intensityID, .tmax, .x1p, -1, diff_left
|
300
|
+
left = max (.left_filt, result) ; select rightmost of both candidates
|
301
|
+
diff_right = max (3, (.max-.dip_intbp)/2 )
|
302
|
+
@get_boundary: intbpID, .tmaxfil, .x2p, 1, diff_right
|
303
|
+
.right_filt = result
|
304
|
+
diff_right = max (3, (.max-.dip_int)/2 )
|
305
|
+
@get_boundary: intensityID, .tmax, .x2p, 1, diff_right
|
306
|
+
right = max (.right_filt, result) ; select rightmost of both candidates
|
307
|
+
right = min (.x2p, right) ; different time unit in get_boundary
|
308
|
+
@voiced_intersection: left, right
|
309
|
+
if (result > 0 and right-left >= mindur_nucl)
|
310
|
+
valid = 1
|
311
|
+
endif
|
312
|
+
endif
|
313
|
+
endproc
|
314
|
+
|
315
|
+
|
316
|
+
procedure find_silences: paramID_, dstID, dst_tier
|
317
|
+
; Use Praat's procedure to identify silent pauses on the basis of <paramID_> (usu. intensity)
|
318
|
+
; and copy results to tier <dst_tier> of <dstID>
|
319
|
+
@debug_msg: "find_silences: entry"
|
320
|
+
selectObject: dstID
|
321
|
+
min_silent_interval = 0.15 ; Praat standard = 0.1
|
322
|
+
min_sounding_interval = 0.05 ; Praat standard = 0.1
|
323
|
+
silence_threshold = -30.0 ; Praat standard = -25.0
|
324
|
+
selectObject: paramID_
|
325
|
+
.silencesID = To TextGrid (silences): silence_threshold, min_silent_interval, min_sounding_interval, "_", "a"
|
326
|
+
Rename: "silences"
|
327
|
+
.n = Get number of intervals: 1
|
328
|
+
; Copy boundaries from silences TextGrid to dstID in time range
|
329
|
+
for .j to .n
|
330
|
+
selectObject: .silencesID
|
331
|
+
.label$ = Get label of interval: 1, .j
|
332
|
+
.x1 = Get start point: 1, .j
|
333
|
+
.x2 = Get end point: 1, .j
|
334
|
+
@add_boundary: dstID, dst_tier, .x1
|
335
|
+
@add_boundary: dstID, dst_tier, .x2
|
336
|
+
@set_boundary_label: dstID, dst_tier, .x1, .x2, .label$
|
337
|
+
endfor
|
338
|
+
removeObject: .silencesID
|
339
|
+
@debug_msg: "find_silences: exit"
|
340
|
+
endproc
|
341
|
+
|
342
|
+
|
343
|
+
procedure voiced_portions: .dstID, .intID, .at1, .at2
|
344
|
+
# Find voiced portions in range <.at1>..<.at2> of signal and store corresponding intervals in nucleus_tier of <.dstID> TextGrid.
|
345
|
+
@debug_msg: "voiced_portions: entry"
|
346
|
+
@find_silences: .intID, .dstID, syllable_tier
|
347
|
+
selectObject: .dstID
|
348
|
+
# number of intervals grows during loop
|
349
|
+
.time = .at1
|
350
|
+
@add_boundary: .dstID, nucleus_tier, .time
|
351
|
+
while (.time + time_step < .at2) ; avoid rounding error
|
352
|
+
selectObject: .dstID
|
353
|
+
.j = Get interval at time: syllable_tier, .time
|
354
|
+
.label$ = Get label of interval: syllable_tier, .j
|
355
|
+
.x1 = Get start time of interval: syllable_tier, .j
|
356
|
+
.x2 = Get end time of interval: syllable_tier, .j
|
357
|
+
.nexttime = .x2
|
358
|
+
;@debug_msg: "voiced_portions: x1='.x1:4' x2='.x2:4'"
|
359
|
+
@add_boundary: .dstID, nucleus_tier, .x2
|
360
|
+
if (.label$ == "_") ; pause
|
361
|
+
@set_boundary_label: .dstID, nucleus_tier, .x1, .x2, .label$
|
362
|
+
else
|
363
|
+
repeat
|
364
|
+
@voiced_intersection: .x1, .x2
|
365
|
+
; returns result, left, right
|
366
|
+
if (result) ; it contains a voiced part
|
367
|
+
if (left-.x1 > time_step)
|
368
|
+
@add_boundary: .dstID, nucleus_tier, left
|
369
|
+
@set_boundary_label: .dstID, nucleus_tier, .x1, left, "U"
|
370
|
+
endif
|
371
|
+
@add_boundary: .dstID, nucleus_tier, right
|
372
|
+
@set_boundary_label: .dstID, nucleus_tier, left, right, "a"
|
373
|
+
.x1 = right
|
374
|
+
else
|
375
|
+
.x1 = .x2
|
376
|
+
endif
|
377
|
+
until (.x1 >= .x2)
|
378
|
+
endif
|
379
|
+
.time = .nexttime
|
380
|
+
endwhile
|
381
|
+
@debug_msg: "voiced_portions: exit"
|
382
|
+
endproc
|
383
|
+
|
384
|
+
|
385
|
+
procedure pseudo_syllables: .dstID, .intID, .ifilID, .at1, .at2, .mindiff
|
386
|
+
@debug_msg: "pseudo_syllables: entry"
|
387
|
+
@find_silences: .intID, .dstID, syllable_tier ; silent intervals are stored as intervals on syllabe tier
|
388
|
+
@mark_unvoiced: .dstID, dip_tier, .at1, .at2 ; voiced-unvoiced transitions are stored as points on dip tier
|
389
|
+
# number of intervals grows during following loop
|
390
|
+
# for each interval in syllable tier (either a silence or a speech fragment)
|
391
|
+
.time = .at1
|
392
|
+
while (.time + time_step < .at2) ; avoid rounding error
|
393
|
+
selectObject: .dstID
|
394
|
+
.j = Get interval at time: syllable_tier, .time
|
395
|
+
.label$ = Get label of interval: syllable_tier, .j
|
396
|
+
.x1 = Get start time of interval: syllable_tier, .j
|
397
|
+
.x2 = Get end time of interval: syllable_tier, .j
|
398
|
+
.nexttime = .x2
|
399
|
+
if (.label$ = "a")
|
400
|
+
@convexhull: .dstID, dip_tier, .ifilID, .x1, .x2, .mindiff ; major dips are stored as points on dip tier
|
401
|
+
if (result)
|
402
|
+
@tier_point_to_interval: .dstID, dip_tier, syllable_tier, .x1, .x2
|
403
|
+
@local_peaks_duo: .dstID, .intID, .ifilID, .x1, .x2
|
404
|
+
endif
|
405
|
+
endif
|
406
|
+
.time = .nexttime
|
407
|
+
endwhile
|
408
|
+
@pseudo_syll_pass4: .intID, .dstID, syllable_tier
|
409
|
+
@debug_msg: "pseudo_syllables: exit"
|
410
|
+
endproc
|
411
|
+
|
412
|
+
|
413
|
+
procedure segmentation_pitch: .pitchID, .thresholdST, .intID, .intbpID, .t1, .t2, .dstID, .mindiff
|
414
|
+
@debug_msg: "segmentation_pitch: entry"
|
415
|
+
@find_silences: .intID, .dstID, syllable_tier ; silence and speech are stored as intervals on syllable tier
|
416
|
+
@mark_unvoiced: .dstID, dip_tier, .t1, .t2 ; voiced-unvoiced transitions are stored as points on dip tier
|
417
|
+
; @all_convexhull: .dstID, syllable_tier, .intID, .intbpID, .t1, .t2, .mindiff
|
418
|
+
@pitch_changes: .pitchID, .t1, .t2, .thresholdST, .dstID, dip_tier
|
419
|
+
@tier_point_to_interval: .dstID, dip_tier, syllable_tier, .t1, .t2
|
420
|
+
@label_syllable_tier: .dstID
|
421
|
+
; @local_peaks_syllables: .dstID, .intID, .t1, .t2
|
422
|
+
@debug_msg: "segmentation_pitch: exit"
|
423
|
+
endproc
|
424
|
+
|
425
|
+
|
426
|
+
procedure segmentation_pitch2: .pitchID, .thresholdST, .intID, .t1, .t2, .mindur, .dstID
|
427
|
+
@debug_msg: "segmentation_pitch2: entry"
|
428
|
+
; dip tier stores end times of terraces, slopes, unvoiced intervals:
|
429
|
+
@pitch_terraces: .pitchID, .t1, .t2, .thresholdST, .mindur, .dstID, dip_tier
|
430
|
+
|
431
|
+
selectObject: .dstID
|
432
|
+
|
433
|
+
; Merge adjacent slope parts
|
434
|
+
.j = Get number of points: dip_tier
|
435
|
+
while (.j > 1)
|
436
|
+
.label$ = Get label of point: dip_tier, .j
|
437
|
+
.prev$ = Get label of point: dip_tier, .j-1
|
438
|
+
if (.label$ == "S" and .prev$ == "S")
|
439
|
+
Remove point: dip_tier, .j-1
|
440
|
+
endif
|
441
|
+
.j -= 1
|
442
|
+
endwhile
|
443
|
+
|
444
|
+
; Convert point tier ("dip") to interval tier (syllables) and label pitch terraces as "a"
|
445
|
+
.n = Get number of points: dip_tier
|
446
|
+
for .j to .n
|
447
|
+
.time = Get time of point: dip_tier, .j
|
448
|
+
.label$ = Get label of point: dip_tier, .j
|
449
|
+
Insert boundary: syllable_tier, .time
|
450
|
+
if (.label$ == "T")
|
451
|
+
.k = Get low interval at time: syllable_tier, .time
|
452
|
+
Set interval text: syllable_tier, .k, "a"
|
453
|
+
endif
|
454
|
+
endfor
|
455
|
+
@debug_msg: "segmentation_pitch2: exit"
|
456
|
+
endproc
|
457
|
+
|
458
|
+
|
459
|
+
procedure spectral_similarity: .soundID, .dstID, .intID, .at1, .at2, .dcorr
|
460
|
+
@debug_msg: "spectral_similarity: entry"
|
461
|
+
@find_silences: .intID, .dstID, syllable_tier ; silence and speech are stored as intervals on syllabe tier
|
462
|
+
@mark_unvoiced: .dstID, dip_tier, .at1, .at2 ; voiced-unvoiced transitions are stored as points on dip tier
|
463
|
+
@spectral_correlation: .soundID, .at1, .at2, 0.03 ; compute correlation between spectral slices; time interval between spectral slices being correlated = 30ms
|
464
|
+
.corrID = result
|
465
|
+
# number of intervals grows during loop
|
466
|
+
.time = .at1
|
467
|
+
while (.time + time_step < .at2) ; avoid rounding error
|
468
|
+
selectObject: .dstID
|
469
|
+
.j = Get interval at time: syllable_tier, .time
|
470
|
+
.label$ = Get label of interval: syllable_tier, .j
|
471
|
+
.x1 = Get start point: syllable_tier, .j
|
472
|
+
.x2 = Get end point: syllable_tier, .j
|
473
|
+
.nexttime = .x2
|
474
|
+
if (.label$ = "a")
|
475
|
+
@convexhull: .dstID, dip_tier, .corrID, .x1, .x2, .dcorr ; major dips are stored as points on dip tier
|
476
|
+
if (result)
|
477
|
+
@tier_point_to_interval: .dstID, dip_tier, syllable_tier, .x1, .x2
|
478
|
+
.t = .x1
|
479
|
+
repeat
|
480
|
+
.i = Get interval at time: syllable_tier, .t
|
481
|
+
Set interval text: syllable_tier, .i, "a"
|
482
|
+
.t = Get end point: syllable_tier, .i
|
483
|
+
until (.t >= .x2)
|
484
|
+
endif
|
485
|
+
endif
|
486
|
+
.time = .nexttime
|
487
|
+
endwhile
|
488
|
+
.ns = Get number of intervals: syllable_tier
|
489
|
+
for .j to .ns
|
490
|
+
selectObject: .dstID
|
491
|
+
.label$ = Get label of interval: syllable_tier, .j
|
492
|
+
if (.label$ = "a")
|
493
|
+
.x1 = Get start point: syllable_tier, .j
|
494
|
+
.x2 = Get end point: syllable_tier, .j
|
495
|
+
@get_local_peak: .dstID, .intID, .x1, .x2, 0, .x1, .x2
|
496
|
+
endif
|
497
|
+
endfor
|
498
|
+
removeObject: .corrID
|
499
|
+
@debug_msg: "spectral_similarity: exit"
|
500
|
+
endproc
|
501
|
+
|
502
|
+
|
503
|
+
procedure spectral_correlation: .soundID, .t1, .t2, .timegap
|
504
|
+
; Compute correlation between successive spectral slices to find point of spectral change.
|
505
|
+
; <.timegap> the time between spectral slices being correlated.
|
506
|
+
; The array of correlation values is casted to an intensity object (the ID od which is returned in <result>,
|
507
|
+
; for later use in convex hull.
|
508
|
+
ncols = (.t2-.t1)/time_step + 1
|
509
|
+
; Create Matrix: name$, xmin, xmax, ncols, dx, x1, ymin, ymax, nrows, dy, y1, formula$
|
510
|
+
.corr = Create Matrix: "correlation", .t1, .t2, ncols, time_step, .t1, 0, 1, 1, 1, 0, "0"
|
511
|
+
selectObject: .soundID
|
512
|
+
; To Cochleagram: timestep, freqresolution(Bark), windowlength, forward_masking_time
|
513
|
+
.cochleaID = To Cochleagram: 0.01, 0.1, 0.03, 0.03
|
514
|
+
selectObject: .soundID
|
515
|
+
|
516
|
+
.time = .t1
|
517
|
+
while (.time < .t2)
|
518
|
+
@cochl_slice: .cochleaID, .time - .timegap/2
|
519
|
+
.tmp1 = result
|
520
|
+
@cochl_slice: .cochleaID, .time + .timegap/2
|
521
|
+
.tmp2 = result
|
522
|
+
selectObject: .tmp1, .tmp2
|
523
|
+
.tmp3 = Append columns
|
524
|
+
.corID = To Correlation
|
525
|
+
.r = Get value: 1, 2
|
526
|
+
selectObject: .corr
|
527
|
+
.col = ((.time-.t1)/time_step)+1
|
528
|
+
Set value: 1, .col, .r
|
529
|
+
removeObject: .tmp1, .tmp2, .tmp3, .corID
|
530
|
+
.time += time_step
|
531
|
+
endwhile
|
532
|
+
selectObject: .corr
|
533
|
+
result = To Intensity
|
534
|
+
removeObject: .cochleaID, .corr
|
535
|
+
endproc
|
536
|
+
|
537
|
+
|
538
|
+
procedure cochl_slice: .cochleaID, .time
|
539
|
+
selectObject: .cochleaID
|
540
|
+
excID = To Excitation (slice): .time
|
541
|
+
matID = To Matrix
|
542
|
+
mattID = Transpose
|
543
|
+
result = To TableOfReal
|
544
|
+
removeObject: excID, matID, mattID
|
545
|
+
endproc
|
546
|
+
|
547
|
+
|
548
|
+
procedure mark_unvoiced: dstID, tier_, at1, at2
|
549
|
+
# add boundaries to <tier_> (point tier) in dstID, at voiced-unvoiced transitions
|
550
|
+
@debug_msg: "mark_unvoiced: entry"
|
551
|
+
selectObject: dstID
|
552
|
+
n_ = Get number of intervals: vuv_tier
|
553
|
+
prev$ = ""
|
554
|
+
time_ = at1
|
555
|
+
while (time_ < at2)
|
556
|
+
j_ = Get interval at time: vuv_tier, time_
|
557
|
+
label_$ = Get label of interval: vuv_tier, j_
|
558
|
+
x2_ = Get end point: vuv_tier, j_
|
559
|
+
if (label_$ = "U" and prev$ = "V")
|
560
|
+
x1_ = Get start point: vuv_tier, j_
|
561
|
+
@tier_point_add: dstID, tier_, x1_, "0"
|
562
|
+
endif
|
563
|
+
prev$ = label_$
|
564
|
+
time_ = x2_
|
565
|
+
if (j_ == n_)
|
566
|
+
time_ = at2 ; exit loop
|
567
|
+
endif
|
568
|
+
endwhile
|
569
|
+
@debug_msg: "mark_unvoiced: exit"
|
570
|
+
endproc
|
571
|
+
|
572
|
+
|
573
|
+
procedure pseudo_syll_pass4: .intID, .dstID, .tier
|
574
|
+
; 1. Group sequences with pattern: [<] a [>]
|
575
|
+
@debug_msg: "pseudo_syll_pass4: entry"
|
576
|
+
selectObject: .dstID
|
577
|
+
.n = Get number of intervals: .tier
|
578
|
+
.j = 2
|
579
|
+
while (.j <= .n)
|
580
|
+
selectObject: .dstID
|
581
|
+
.label$ = Get label of interval: .tier, .j
|
582
|
+
if (.label$ = "a")
|
583
|
+
.x1 = Get start time of interval: .tier, .j
|
584
|
+
.x2 = Get end time of interval: .tier, .j
|
585
|
+
selectObject: .intID
|
586
|
+
.ymax = Get maximum: .x1, .x2, "Parabolic"
|
587
|
+
selectObject: .dstID
|
588
|
+
Set interval text: .tier, .j, "syl"
|
589
|
+
if (.j > 1)
|
590
|
+
.prevlabel$ = Get label of interval: .tier, .j-1
|
591
|
+
if (.prevlabel$ = "<")
|
592
|
+
Remove left boundary: .tier, .j
|
593
|
+
.j -= 1
|
594
|
+
.n -= 1
|
595
|
+
Set interval text: .tier, .j, "syl"
|
596
|
+
endif
|
597
|
+
endif
|
598
|
+
if (.j < .n)
|
599
|
+
.nextlabel$ = Get label of interval: .tier, .j+1
|
600
|
+
.x = Get end time of interval: .tier, .j
|
601
|
+
selectObject: .intID
|
602
|
+
.y = Get value at time: .x, "Nearest"
|
603
|
+
if (.nextlabel$ = ">" and .ymax - .y < 25)
|
604
|
+
selectObject: .dstID
|
605
|
+
Remove right boundary: .tier, .j
|
606
|
+
.n -= 1
|
607
|
+
Set interval text: .tier, .j, "syl"
|
608
|
+
endif
|
609
|
+
endif
|
610
|
+
endif
|
611
|
+
.j += 1
|
612
|
+
endwhile
|
613
|
+
; 2. Group unvoiced rejected nuclei with next syllable
|
614
|
+
.j = 1
|
615
|
+
while (.j <= .n)
|
616
|
+
selectObject: .dstID
|
617
|
+
.label$ = Get label of interval: .tier, .j
|
618
|
+
if (.label$ = "syl")
|
619
|
+
if (.j > 1)
|
620
|
+
.prevlabel$ = Get label of interval: .tier, .j-1
|
621
|
+
if (.prevlabel$ = "U")
|
622
|
+
Remove left boundary: .tier, .j
|
623
|
+
.n -= 1
|
624
|
+
.j -= 1
|
625
|
+
Set interval text: .tier, .j, "syl"
|
626
|
+
endif
|
627
|
+
endif
|
628
|
+
endif
|
629
|
+
.j += 1
|
630
|
+
endwhile
|
631
|
+
@debug_msg: "pseudo_syll_pass4: exit"
|
632
|
+
endproc
|
633
|
+
|
634
|
+
|
635
|
+
procedure get_local_peak: dstID, .paramID, x1, x2, dyn_threshold, seed_x1, seed_x2
|
636
|
+
# Find maximum in interval <seed_x1>..<seed_x2>. Find boundaries inside <x1>..<x2>.
|
637
|
+
# Time ranges <x1>..<x2> and <seed_x1>..<seed_x2> may be identical.
|
638
|
+
# <dyn_threshold> Use dynamic intensity threshold for right boundary
|
639
|
+
@debug_msg: "get_local_peak: entry x1='x1:3' x2='x2:3'"
|
640
|
+
selectObject: .paramID
|
641
|
+
time_step = Get time step
|
642
|
+
; default values used in case of error
|
643
|
+
left = x1 ; left boundary of peak
|
644
|
+
right = x2 ; right boundary of peak
|
645
|
+
label2$ = "-" ; not a nucleus
|
646
|
+
valid = 0 ; not a valid nucleus
|
647
|
+
@is_unvoiced_region: dstID, x1, x2
|
648
|
+
if (result = 0) ; not fully unvoiced, i.e. partly voiced
|
649
|
+
selectObject: .paramID
|
650
|
+
maxtime = Get time of maximum: seed_x1, seed_x2, "Parabolic"
|
651
|
+
max = Get maximum: seed_x1, seed_x2, "Parabolic"
|
652
|
+
if (max = undefined) ; can happen at both ends of signal, where intensity is undefined
|
653
|
+
@msg: "get_local_peak: max undefined at 'maxtime:3', seed_x1='seed_x1:3' seed_x2='seed_x2:3'"
|
654
|
+
# use defaults
|
655
|
+
else ; max is defined
|
656
|
+
# find left boundary
|
657
|
+
dipL = Get value at time: x1, "Nearest"
|
658
|
+
if (dipL = undefined)
|
659
|
+
@msg: "get_local_peak: dip left undefined at time 'x1:3'"
|
660
|
+
else
|
661
|
+
; <diff_left> is initialized in @initialization_multiple_files (prosomain.praat)
|
662
|
+
; diff_left = 3
|
663
|
+
@get_boundary: .paramID, maxtime, x1, -1, diff_left
|
664
|
+
left = result
|
665
|
+
if ((segm_method == segm_vnucl or segm_method == segm_mrhyme) and left-seed_x1 >= 0.075)
|
666
|
+
left = seed_x1 + 0.02
|
667
|
+
endif
|
668
|
+
endif
|
669
|
+
# find right boundary
|
670
|
+
dip = Get minimum: maxtime, x2, "Parabolic"
|
671
|
+
if (dip = undefined)
|
672
|
+
@msg: "get_local_peak: dip right undefined at time 'x2:3'"
|
673
|
+
else
|
674
|
+
diff_right = 9
|
675
|
+
if (dyn_threshold)
|
676
|
+
diff_right = max(3, (max - dip)*0.80)
|
677
|
+
endif
|
678
|
+
@get_boundary: .paramID, maxtime, x2, 1, diff_right
|
679
|
+
right = result
|
680
|
+
endif
|
681
|
+
if (dipL != undefined and dip != undefined)
|
682
|
+
@voiced_intersection: left, right
|
683
|
+
if (result > 0 and right-left >= mindur_nucl)
|
684
|
+
label2$ = "a"
|
685
|
+
valid = 1
|
686
|
+
endif
|
687
|
+
endif
|
688
|
+
endif
|
689
|
+
endif ; voiced
|
690
|
+
;@msg: "get_local_peak: ['seed_x1:3' 'seed_x2:3'] peak='maxtime:3' ADDING boundaries L='left:3' R='right:3' label='label2$'"
|
691
|
+
@add_boundary: dstID, nucleus_tier, left
|
692
|
+
@add_boundary: dstID, nucleus_tier, right
|
693
|
+
@set_boundary_label: dstID, nucleus_tier, left, right, label2$
|
694
|
+
if (valid)
|
695
|
+
@add_boundary: dstID, nucleus_tier, x1
|
696
|
+
@add_boundary: dstID, nucleus_tier, x2
|
697
|
+
if (left-x1 > time_step)
|
698
|
+
@set_boundary_label: dstID, nucleus_tier, x1, left, "<"
|
699
|
+
endif
|
700
|
+
if (x2-right > time_step)
|
701
|
+
@set_boundary_label: dstID, nucleus_tier, right, x2, ">"
|
702
|
+
endif
|
703
|
+
endif ; valid
|
704
|
+
endproc
|
705
|
+
|
706
|
+
|
707
|
+
procedure all_convexhull: .gridID, .tier, .paramID, .t1, .t2, .mindiff
|
708
|
+
# Apply convexhull to each interval of tier in grid, storing major dips in dip_tier
|
709
|
+
.time = .t1
|
710
|
+
while (.time + time_step < .t2) ; avoid rounding error
|
711
|
+
selectObject: .gridID
|
712
|
+
.j = Get interval at time: .tier, .time
|
713
|
+
.label$ = Get label of interval: .tier, .j
|
714
|
+
.x1 = Get start point: .tier, .j
|
715
|
+
.x2 = Get end point: .tier, .j
|
716
|
+
if (.label$ = "a")
|
717
|
+
@convexhull: .gridID, dip_tier, .paramID, .x1, .x2, .mindiff
|
718
|
+
endif
|
719
|
+
.time = .x2
|
720
|
+
endwhile
|
721
|
+
endproc
|
722
|
+
|
723
|
+
|
724
|
+
procedure convexhull: .gridID, .tier, .paramID, .x1, .x2, .mindiff
|
725
|
+
# <.gridID> textgrid in which segmentation points are stored
|
726
|
+
# <.tier> point tier where segmentation points are stored
|
727
|
+
# <.paramID> parameter on which segmentation is based
|
728
|
+
# <.x1> start time of analysis
|
729
|
+
# <.x2> end time of analysis
|
730
|
+
# <.mindiff> difference threshold for segmentation
|
731
|
+
# <result>
|
732
|
+
@debug_msg: "convexhull: entry"
|
733
|
+
convexhull_winlen = 0.75
|
734
|
+
selectObject: .paramID
|
735
|
+
.dx = Get time step
|
736
|
+
|
737
|
+
# Skip part at start of signal for which parameter is undefined
|
738
|
+
.ok_L = 0
|
739
|
+
.xL = .x1
|
740
|
+
repeat
|
741
|
+
.y = Get value at time: .xL, "Nearest"
|
742
|
+
if (.y == undefined)
|
743
|
+
.xL += .dx
|
744
|
+
else
|
745
|
+
.ok_L = 1
|
746
|
+
endif
|
747
|
+
until (.ok_L or .xL > .x2)
|
748
|
+
|
749
|
+
# Skip part at end of signal for which parameter is undefined
|
750
|
+
.ok_R = 0
|
751
|
+
.xR = .x2
|
752
|
+
repeat
|
753
|
+
.y = Get value at time: .xR, "Nearest"
|
754
|
+
if (.y == undefined)
|
755
|
+
.xR -= .dx
|
756
|
+
else
|
757
|
+
.ok_R = 1
|
758
|
+
endif
|
759
|
+
until (.ok_R or .xR < .x1)
|
760
|
+
|
761
|
+
result = 0
|
762
|
+
if (.ok_L and .ok_R)
|
763
|
+
while (.xL < .xR)
|
764
|
+
.xlast = min (.xL + convexhull_winlen, .xR)
|
765
|
+
.dip = 0
|
766
|
+
repeat
|
767
|
+
@time_maxdiff: .paramID, .xL, .xlast, .dx, .mindiff
|
768
|
+
if (tmaxdif >= 0)
|
769
|
+
.xlast = tmaxdif
|
770
|
+
.dip = maxdif
|
771
|
+
endif
|
772
|
+
until (tmaxdif < 0)
|
773
|
+
if (.dip > .mindiff)
|
774
|
+
;@msg: "ADDING DIP at '.xlast:4', diff='.dip:3'"
|
775
|
+
@tier_point_add: .gridID, .tier, .xlast, "'.dip:1'"
|
776
|
+
endif
|
777
|
+
.xL = .xlast ; shift start of analysis window to end of previous
|
778
|
+
endwhile
|
779
|
+
result = 1
|
780
|
+
endif
|
781
|
+
@debug_msg: "convexhull: exit"
|
782
|
+
endproc
|
783
|
+
|
784
|
+
|
785
|
+
procedure time_maxdiff: paramID, xh1, xh2, dx, mindif
|
786
|
+
# returns <tmaxdif>, <maxdif>, <result>
|
787
|
+
# <result> (boolean) true if dip found (dif >= mindif)
|
788
|
+
selectObject: paramID
|
789
|
+
maxdif = 0.0
|
790
|
+
tmax_ = Get time of maximum... xh1 xh2 Parabolic
|
791
|
+
if (tmax_ > xh1)
|
792
|
+
x = xh1
|
793
|
+
tmaxdif = xh1
|
794
|
+
h = Get value at time... x Nearest
|
795
|
+
while (x < tmax_) ; locate max diff while going up hull to peak
|
796
|
+
y = Get value at time... x Nearest
|
797
|
+
if (h-y > maxdif)
|
798
|
+
maxdif = h-y
|
799
|
+
tmaxdif = x
|
800
|
+
endif
|
801
|
+
h = max (y,h)
|
802
|
+
x += dx
|
803
|
+
endwhile
|
804
|
+
endif
|
805
|
+
if (tmax_ < xh2)
|
806
|
+
x = xh2
|
807
|
+
h = Get value at time... x Nearest
|
808
|
+
while (x > tmax_)
|
809
|
+
y = Get value at time... x Nearest
|
810
|
+
if (h-y > maxdif)
|
811
|
+
maxdif = h-y
|
812
|
+
tmaxdif = x
|
813
|
+
endif
|
814
|
+
h = max (y,h)
|
815
|
+
x -= dx
|
816
|
+
endwhile
|
817
|
+
endif
|
818
|
+
if (maxdif >= mindif)
|
819
|
+
result = 1
|
820
|
+
else
|
821
|
+
result = 0
|
822
|
+
tmaxdif = -1
|
823
|
+
endif
|
824
|
+
endproc
|
825
|
+
|
826
|
+
|
827
|
+
procedure add_boundary: .destID, .tier, .xbound
|
828
|
+
# Add a boundary, but
|
829
|
+
# - avoid adding left boundary at right boundary of previous segment
|
830
|
+
# - avoid adding boundary at starttime of TextGrid
|
831
|
+
# - avoid adding boundary at endtime of TextGrid
|
832
|
+
# <.xbound> time of boundary
|
833
|
+
;@debug_msg: "add_boundary: entry"
|
834
|
+
selectObject: .destID
|
835
|
+
.xstop = Get end time
|
836
|
+
.xstart = Get start time
|
837
|
+
.i = Get interval at time: .tier, .xbound
|
838
|
+
if (.i <= 0)
|
839
|
+
@msg: "add_boundary: i<=0 xbound='.xbound'"
|
840
|
+
endif
|
841
|
+
.t1 = Get start time of interval: .tier, .i
|
842
|
+
.t2 = Get end time of interval: .tier, .i
|
843
|
+
if (abs(.t1-.xbound) > time_step and abs(.t2-.xbound) > time_step and .xbound > .xstart and .xbound < .xstop)
|
844
|
+
Insert boundary: .tier, .xbound
|
845
|
+
endif
|
846
|
+
;@debug_msg: "add_boundary: exit"
|
847
|
+
endproc
|
848
|
+
|
849
|
+
|
850
|
+
procedure tier_point_add: .destID, .tier, .x, .text$
|
851
|
+
selectObject: .destID
|
852
|
+
.i = Get nearest index from time: .tier, .x
|
853
|
+
if (.i == 0) ; no points in tier
|
854
|
+
Insert point: .tier, .x, .text$
|
855
|
+
else
|
856
|
+
.t = Get time of point: .tier, .i
|
857
|
+
if (.t == .x)
|
858
|
+
@msg: "tier_point_add: already a point at time '.x:3'"
|
859
|
+
else
|
860
|
+
Insert point: .tier, .x, .text$
|
861
|
+
endif
|
862
|
+
endif
|
863
|
+
endproc
|
864
|
+
|
865
|
+
|
866
|
+
procedure set_boundary_label: .destID, .tier, .xleft, .xright, .label$
|
867
|
+
;call debug_msg set_boundary_label: entry
|
868
|
+
select .destID
|
869
|
+
.i = Get interval at time: .tier, .xleft+(.xright-.xleft)/2
|
870
|
+
Set interval text: .tier, .i, .label$
|
871
|
+
;call debug_msg set_boundary_label: exit
|
872
|
+
endproc
|
873
|
+
|
874
|
+
|
875
|
+
procedure get_boundary_old: .paramID, .peaktime, .xlimit, .incr, .diff
|
876
|
+
# Get time of left or right boundary.
|
877
|
+
# <.peaktime> time of peak in interval
|
878
|
+
# <.xlimit> time limit for left or right boundary
|
879
|
+
# <.incr> -1 for left boundary, 1 for right boundary
|
880
|
+
# <.diff> max difference between peak and value at boundary
|
881
|
+
# Returns time of left or right boundary in <result>
|
882
|
+
result = 0
|
883
|
+
selectObject: .paramID
|
884
|
+
.peakframe = Get frame number from time: .peaktime
|
885
|
+
.i = round (.peakframe)
|
886
|
+
.max = Get value in frame: .i
|
887
|
+
.limit = Get frame number from time: .xlimit
|
888
|
+
.limit = round (.limit)
|
889
|
+
;@msg: "get_boundary: t='.peaktime:4' xlimit='.xlimit:5' limit='.limit'"
|
890
|
+
.ok = 1
|
891
|
+
while (.ok)
|
892
|
+
.nexti = .i + .incr
|
893
|
+
.y = Get value in frame: .i
|
894
|
+
if ((.incr < 0 and .nexti < .limit) or (.incr > 0 and .nexti > .limit) or (.max-.y > .diff))
|
895
|
+
.ok = 0
|
896
|
+
else
|
897
|
+
.i = .nexti
|
898
|
+
if (.incr < 0 and .i <= 0)
|
899
|
+
.ok = 0
|
900
|
+
.i = 1
|
901
|
+
endif
|
902
|
+
endif
|
903
|
+
endwhile
|
904
|
+
result = Get time from frame: .i
|
905
|
+
if (.incr > 0) ; frame is real number -> rounding errors
|
906
|
+
result = min (result, .xlimit)
|
907
|
+
else
|
908
|
+
result = max (result, .xlimit)
|
909
|
+
endif
|
910
|
+
endproc
|
911
|
+
|
912
|
+
|
913
|
+
procedure get_boundary: .paramID, .peaktime, .xlimit, .incr, .diff
|
914
|
+
# Get time of left or right boundary.
|
915
|
+
# <.peaktime> time of peak in interval
|
916
|
+
# <.xlimit> time limit for left or right boundary
|
917
|
+
# <.incr> -1 for left boundary, 1 for right boundary
|
918
|
+
# <.diff> max difference between peak and value at boundary
|
919
|
+
# Returns time of left or right boundary in <result>
|
920
|
+
selectObject: .paramID
|
921
|
+
.dx = Get time step
|
922
|
+
.t = .peaktime
|
923
|
+
.max = Get value at time: .t, "Nearest"
|
924
|
+
.ok = 1
|
925
|
+
while (.ok)
|
926
|
+
.y = Get value at time: .t, "Nearest"
|
927
|
+
.tn = .t + (.incr * .dx)
|
928
|
+
if ((.incr < 0 and .tn < .xlimit) or (.incr > 0 and .tn > .xlimit) or (.max-.y > .diff))
|
929
|
+
.ok = 0
|
930
|
+
else
|
931
|
+
.t = .tn
|
932
|
+
if (.incr < 0 and .t <= 0)
|
933
|
+
.ok = 0
|
934
|
+
.t = 0
|
935
|
+
endif
|
936
|
+
endif
|
937
|
+
endwhile
|
938
|
+
;@msg: "get_boundary: tp='.peaktime:4' xlimit='.xlimit:5' t='.t:5' incr='.incr'"
|
939
|
+
if (.incr > 0)
|
940
|
+
result = min (.t, .xlimit)
|
941
|
+
else
|
942
|
+
result = max (.t, .xlimit)
|
943
|
+
endif
|
944
|
+
endproc
|
945
|
+
|
946
|
+
|
947
|
+
|
948
|
+
procedure tier_point_to_interval: .gridID, .ptier, .itier, .t1, .t2
|
949
|
+
# Convert points in PointTier <ptier> to intervals in <itier>
|
950
|
+
@debug_msg: "tier_point_to_interval: entry"
|
951
|
+
selectObject: .gridID
|
952
|
+
.n = Get number of points: .ptier
|
953
|
+
for .j to .n
|
954
|
+
.time = Get time of point: .ptier, .j
|
955
|
+
if (.time >= .t1 and .time <= .t2)
|
956
|
+
@add_boundary: .gridID, .itier, .time
|
957
|
+
endif
|
958
|
+
endfor
|
959
|
+
@debug_msg: "tier_point_to_interval: exit"
|
960
|
+
endproc
|
961
|
+
|
962
|
+
|
963
|
+
procedure is_unvoiced_region: .gridID, .x1, .x2
|
964
|
+
# Returns 1 in variable <result> if <.x1> and <.x2> are within same unvoiced interval of the VUV grid
|
965
|
+
@debug_msg: "is_unvoiced_region: entry"
|
966
|
+
result = 0
|
967
|
+
selectObject: .gridID
|
968
|
+
.i1 = Get interval at time: vuv_tier, .x1
|
969
|
+
if (.i1 == 0) ; peeking before analysed signal; return unvoiced
|
970
|
+
@msg: "is_unvoiced_region: i1='.i1' x1='x1'"
|
971
|
+
result = 1
|
972
|
+
else
|
973
|
+
.i2 = Get interval at time: vuv_tier, .x2
|
974
|
+
.label$ = Get label of interval: vuv_tier, .i1
|
975
|
+
if (.i1 == .i2 and .label$ = "U")
|
976
|
+
result = 1
|
977
|
+
endif
|
978
|
+
endif
|
979
|
+
@debug_msg: "is_unvoiced_region: exit"
|
980
|
+
endproc
|
981
|
+
|
982
|
+
|
983
|
+
procedure unvoiced_proportion x1_ x2_
|
984
|
+
# returns proportion of unvoiced part inside <x1_>..<x2_>
|
985
|
+
# returns left, right: the unvoiced part inside <x1_>..<x2_>
|
986
|
+
result = 0
|
987
|
+
select nucleiID
|
988
|
+
ni_ = Get number of intervals... vuv_tier
|
989
|
+
i_ = Get interval at time... vuv_tier x1_
|
990
|
+
if (i_ == 0)
|
991
|
+
# peeking outside analysed signal; return unvoiced
|
992
|
+
call msg error in unvoiced_proportion i='i_' x1='x1_'
|
993
|
+
result = 0
|
994
|
+
else
|
995
|
+
ux1 = x2_
|
996
|
+
ux2 = ux1
|
997
|
+
repeat
|
998
|
+
label_$ = Get label of interval... vuv_tier i_
|
999
|
+
t1_ = Get start point... vuv_tier i_
|
1000
|
+
t2_ = Get end point... vuv_tier i_
|
1001
|
+
if (label_$ = "U")
|
1002
|
+
ux1 = max(x1_, t1_)
|
1003
|
+
endif
|
1004
|
+
i_ += 1
|
1005
|
+
until (ux1 < x2_ or t1_ >= x2_ or i_ >= ni_)
|
1006
|
+
ux2 = min (t2_, x2_)
|
1007
|
+
result = (ux2 - ux1) / (x2_ - x1_)
|
1008
|
+
endif
|
1009
|
+
endproc
|
1010
|
+
|
1011
|
+
|
1012
|
+
procedure voiced_intersection: .x1, .x2
|
1013
|
+
# returns 1 in <result> if there is a voiced part inside <.x1>..<.x2>
|
1014
|
+
# returns <left>, <right>: the voiced part inside <.x1>..<.x2>
|
1015
|
+
result = 0
|
1016
|
+
selectObject: nucleiID
|
1017
|
+
.i1 = Get interval at time: vuv_tier, .x1
|
1018
|
+
.i2 = Get interval at time: vuv_tier, .x2
|
1019
|
+
if (.i1 == 0 or .i2 == 0) ; peeking outside analysed signal; return unvoiced
|
1020
|
+
@error_msg: "voiced_intersection: .i1='.i1' x1='.x1'"
|
1021
|
+
else
|
1022
|
+
while (.i1 <= .i2)
|
1023
|
+
.label$ = Get label of interval: vuv_tier, .i1
|
1024
|
+
if (.label$ = "V")
|
1025
|
+
if (result == 0)
|
1026
|
+
.t = Get start time of interval: vuv_tier, .i1
|
1027
|
+
left = max (.t, .x1)
|
1028
|
+
endif
|
1029
|
+
.t = Get end time of interval: vuv_tier, .i1
|
1030
|
+
right = min (.x2, .t)
|
1031
|
+
result = 1
|
1032
|
+
else
|
1033
|
+
if (result == 1)
|
1034
|
+
.i1 = .i2+1
|
1035
|
+
endif
|
1036
|
+
endif
|
1037
|
+
.i1 += 1
|
1038
|
+
endwhile
|
1039
|
+
endif
|
1040
|
+
endproc
|
1041
|
+
|
1042
|
+
|
1043
|
+
procedure is_vowel: s$
|
1044
|
+
# Sets global variable <is_vowel> to 1 if <s$> is a vowel (in SAMPA or Praat Phonetic symbols conventions)
|
1045
|
+
# and to 0 otherwise
|
1046
|
+
.input$ = s$
|
1047
|
+
is_vowel = 0
|
1048
|
+
; remove some symbols, mainly diacritics
|
1049
|
+
s$ = replace_regex$ (s$, "^""", "", 1) ; primary stress (at start of label) in SAMPA
|
1050
|
+
s$ = replace$ (s$, "`", "", 0) ; remove all rhoticity diacritics
|
1051
|
+
s$ = replace_regex$ (s$, "^i_d$", "i", 1) ; dental i in X-SAMPA
|
1052
|
+
s$ = replace_regex$ (s$, ":+$", "", 1) ; diacritic(s) indicating lengthening (at end of label)
|
1053
|
+
s$ = replace$ (s$, "\:f", "", 0) ; diacritic indicating lengthening
|
1054
|
+
len = length (s$)
|
1055
|
+
first$ = left$ (s$, 1)
|
1056
|
+
if (index_regex (s$, "^[aeiouyAEIOUYOQV@23679&\{\}]+$") ) ; SAMPA, 1 or more vocalic elements
|
1057
|
+
is_vowel = 1
|
1058
|
+
elsif (len = 1 and s$ = "V") ; ad hoc convention (on special request)
|
1059
|
+
is_vowel = 1
|
1060
|
+
elsif (len = 2)
|
1061
|
+
if (index ("~a~e~o~E~O~A~9~U~", s$)) ; SAMPA nasal vowels; U~ used by some
|
1062
|
+
is_vowel = 1
|
1063
|
+
endif
|
1064
|
+
elsif (len = 3 and first$ = "\" ) ; Praat phonetic symbols
|
1065
|
+
z$ = mid$ (s$,1,3) ; first three characters
|
1066
|
+
z2$ = mid$ (s$,1,2) ; first two characters
|
1067
|
+
third$ = mid$ (s$, 3, 1)
|
1068
|
+
if (third$ = """" and (index (":\a:\e:\i:\o:\u:\y:", ":'z2$':")))
|
1069
|
+
is_vowel = 1
|
1070
|
+
elsif (third$ = "-" and (index (":\e:\i:\o:\u:", ":'z2$':")))
|
1071
|
+
is_vowel = 1
|
1072
|
+
elsif (index (":\a~:\o~:", ":'s$':")) ; nasal vowels
|
1073
|
+
is_vowel = 1
|
1074
|
+
elsif (index (":\o/:\ab:\as:\ae:\at:\ep:\ef:\er:\oe:\Oe:\ct:\vt:\ic:\yc:\sw:\sr:\rh:\hs:\kb:\mt:\u-:", ":'z$':"))
|
1075
|
+
is_vowel = 1
|
1076
|
+
endif
|
1077
|
+
elsif (len > 3) ; Praat symbols
|
1078
|
+
if (index (":a\~^:\ep~:\as\~^:\ep\~^:\ct\~^:", ":'s$':")) ; nasalized
|
1079
|
+
is_vowel = 1
|
1080
|
+
elsif (index (":a\ic:\ct\ic:a\hs:o\hs:\ct\hs:", ":'s$':")) ; diphthongs aI, OI, aU, oU, OU
|
1081
|
+
is_vowel = 1
|
1082
|
+
endif
|
1083
|
+
endif
|
1084
|
+
endproc
|
1085
|
+
|
1086
|
+
|
1087
|
+
procedure is_syllabic: s$
|
1088
|
+
; sets <result> = true is string is vowel or syllabic consonant
|
1089
|
+
; sets <is_vowel> = true if string is a vowel
|
1090
|
+
@is_vowel: s$
|
1091
|
+
if (is_vowel)
|
1092
|
+
result = 1
|
1093
|
+
; elsif (index_regex (s$, "^[mnJNlrR]\\\|v$")) ; Praat phonetic symbol "\|v"
|
1094
|
+
elsif (index_regex (s$, "^[mnJNlrR]=$")) ; X-SAMPA symbol "="
|
1095
|
+
result = 1
|
1096
|
+
else
|
1097
|
+
result = 0
|
1098
|
+
endif
|
1099
|
+
endproc
|
1100
|
+
|
1101
|
+
|
1102
|
+
procedure pitch_changes: .pitchID, .t1, .t2, .thresholdST, .grid, .tier
|
1103
|
+
# Detect pitch changes exceeding <.thresholdST> (in semitones) and store them as points in tier <.tier> of <.grid>
|
1104
|
+
selectObject: .pitchID
|
1105
|
+
.n = Get number of frames
|
1106
|
+
.j = Get frame number from time: .t1
|
1107
|
+
.j = max (.j, 1)
|
1108
|
+
.j2 = Get frame number from time: .t2
|
1109
|
+
.j2 = min (floor(.j2), .n)
|
1110
|
+
.prevHz = Get value in frame: .j, "Hertz"
|
1111
|
+
while (.j < .j2)
|
1112
|
+
.pitchHz = Get value in frame: .j, "Hertz"
|
1113
|
+
if (.pitchHz == undefined)
|
1114
|
+
repeat ; find end of part with undefined pitch
|
1115
|
+
.right = .j
|
1116
|
+
.j += 1
|
1117
|
+
.nextHz = Get value in frame: .j, "Hertz"
|
1118
|
+
until (.j == .j2 or not (.nextHz == undefined))
|
1119
|
+
else
|
1120
|
+
repeat ; find point where pitch change exceeds threshold or where pitch is undefined
|
1121
|
+
.right = .j
|
1122
|
+
.j += 1
|
1123
|
+
.nextHz = Get value in frame: .j, "Hertz"
|
1124
|
+
.distST = 12 * log2 (.nextHz/.prevHz)
|
1125
|
+
until (.nextHz == undefined or .j == .j2 or abs(.distST) > .thresholdST)
|
1126
|
+
endif
|
1127
|
+
.t = Get time from frame number: .right
|
1128
|
+
selectObject: .grid
|
1129
|
+
Insert point: .tier, .t, ""
|
1130
|
+
selectObject: .pitchID
|
1131
|
+
.prevHz = Get value in frame: .j, "Hertz"
|
1132
|
+
endwhile
|
1133
|
+
endproc
|
1134
|
+
|
1135
|
+
|
1136
|
+
procedure pitch_terraces: .pitchID, .t1, .t2, .thresholdST, .mindur, .grid, .tier
|
1137
|
+
# Detect pitch changes exceeding <.thresholdST> (in semitones per frame period) and store them as points in tier <.tier> of <.grid>
|
1138
|
+
selectObject: .pitchID
|
1139
|
+
.n = Get number of frames
|
1140
|
+
; .prevt = Get start time
|
1141
|
+
; Synchronize times with frame times
|
1142
|
+
.j = Get frame number from time: .t1
|
1143
|
+
.j = max (.j, 1)
|
1144
|
+
.j2 = Get frame number from time: .t2
|
1145
|
+
.j2 = min (floor(.j2), .n)
|
1146
|
+
.yHz = Get value in frame: .j, "Hertz"
|
1147
|
+
; possible states: U=unvoiced, T=(in )terrace, S=(in )slope
|
1148
|
+
; initial states: U, T
|
1149
|
+
if (.yHz == undefined)
|
1150
|
+
.state$ = "U"
|
1151
|
+
else
|
1152
|
+
.state$ = "T"
|
1153
|
+
endif
|
1154
|
+
while (.j < .j2)
|
1155
|
+
.t = Get time from frame number: .j
|
1156
|
+
.switch = 0 ; 1 when change state
|
1157
|
+
.yHz = Get value in frame: .j, "Hertz"
|
1158
|
+
.j += 1
|
1159
|
+
.nextHz = Get value in frame: .j, "Hertz"
|
1160
|
+
.dist = 12 * log2 (.nextHz/.yHz)
|
1161
|
+
; printline *** t='.t:3' state='.state$' j='.j' nextF0='.nextHz:0' dist='.dist:2'
|
1162
|
+
if (.state$ == "T")
|
1163
|
+
if (.nextHz == undefined)
|
1164
|
+
.switch = 1
|
1165
|
+
.newstate$ = "U"
|
1166
|
+
elsif (abs(.dist) > .thresholdST)
|
1167
|
+
.switch = 1
|
1168
|
+
.newstate$ = "S"
|
1169
|
+
endif
|
1170
|
+
elsif (.state$ == "S")
|
1171
|
+
if (.nextHz == undefined)
|
1172
|
+
.switch = 1
|
1173
|
+
.newstate$ = "U"
|
1174
|
+
elsif (abs(.dist) <= .thresholdST)
|
1175
|
+
.switch = 1
|
1176
|
+
.newstate$ = "T"
|
1177
|
+
endif
|
1178
|
+
elsif (.state$ == "U")
|
1179
|
+
if not (.nextHz == undefined)
|
1180
|
+
.switch = 1
|
1181
|
+
.newstate$ = "T"
|
1182
|
+
endif
|
1183
|
+
endif
|
1184
|
+
if (.switch)
|
1185
|
+
.t = Get time from frame number: .j-1
|
1186
|
+
selectObject: .grid
|
1187
|
+
Insert point: .tier, .t, .state$
|
1188
|
+
.state$ = .newstate$
|
1189
|
+
selectObject: .pitchID
|
1190
|
+
endif
|
1191
|
+
endwhile
|
1192
|
+
selectObject: .pitchID
|
1193
|
+
.t = Get time from frame number: .j2
|
1194
|
+
selectObject: .grid
|
1195
|
+
Insert point: .tier, .t, .state$
|
1196
|
+
|
1197
|
+
; Remove short duration pitch terraces (< .mindur)
|
1198
|
+
selectObject: .grid
|
1199
|
+
.j = Get number of points: dip_tier
|
1200
|
+
while (.j > 1)
|
1201
|
+
.label$ = Get label of point: dip_tier, .j
|
1202
|
+
.x2 = Get time of point: dip_tier, .j
|
1203
|
+
.x1 = Get time of point: dip_tier, .j-1
|
1204
|
+
if (.label$ == "T" and .x2-.x1 < .mindur)
|
1205
|
+
Set point text: dip_tier, .j, "D"
|
1206
|
+
endif
|
1207
|
+
.j -= 1
|
1208
|
+
endwhile
|
1209
|
+
endproc
|
1210
|
+
|
1211
|
+
|
1212
|
+
procedure label_syllable_tier: .gridID
|
1213
|
+
selectObject: .gridID
|
1214
|
+
.n = Get number of intervals: syllable_tier
|
1215
|
+
for .j to .n
|
1216
|
+
.x1 = Get start point: syllable_tier, .j
|
1217
|
+
.x2 = Get end point: syllable_tier, .j
|
1218
|
+
@is_unvoiced_region: .gridID, .x1, .x2
|
1219
|
+
if (not result)
|
1220
|
+
Set interval text: syllable_tier, .j, "a"
|
1221
|
+
endif
|
1222
|
+
endfor
|
1223
|
+
endproc
|
1224
|
+
|