-
Notifications
You must be signed in to change notification settings - Fork 35
/
nasal_earbud_extract.praat
272 lines (217 loc) · 7.71 KB
/
nasal_earbud_extract.praat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# Nasal Earbud Extraction Script
# This script is designed to extract information pulled from Nasal Earbud Measument (as described in Stewart and Kohlberger 2017)
# References
## Stewart, J., & Kohlberger, M. (2017). Earbuds: A Method for Analyzing Nasality in the Field.
form De-modulate and Process Airflow Signals
comment Which channel contains nasal earbud signal?
integer naschannel 2
comment Which channel contains acoustic data?
integer orchannel 1
comment Which tier contains nasal segment marking?
integer nastier 2
endform
tpnum = 25
shownasalance = 1
directory$ = chooseDirectory$ ("Choose the directory containing flow files and grids")
directory$ = "'directory$'" + "/"
file_type$ = ".wav"
createDirectory: directory$ + "_graphs"
Create Strings as file list... list 'directory$'*'file_type$'
number_files = Get number of strings
# Open a file for the results
resultfile$ = "'directory$'"+"_flowlog.txt"
header_row$ = "filename" + tab$ + "segment" + tab$ + "point" + tab$ + "vwlpct" + tab$ + "nas_power" + tab$ + "acoustic_power" + tab$ + "percnasalance" + tab$ + "measurement_time"
pheader_row$ = "'header_row$'" + newline$
fileappend "'resultfile$'" 'pheader_row$'
for ifile to number_files
select Strings list
sound$ = Get string... ifile
Read from file... 'directory$''sound$'
soundname$ = selected$ ("Sound", 1)
gridfile$ = "'directory$''soundname$'.TextGrid"
if fileReadable (gridfile$)
# Open the Textgrid
Read from file... 'gridfile$'
number_intervals = Get number of intervals... 'nastier'
###
# Get Grid Information
###
for k from 1 to number_intervals
select TextGrid 'soundname$'
int_label$ = Get label of interval... 'nastier' 'k'
#checks if interval has a label
if int_label$ <> ""
# Calc start, end, and duration of interval
intstart = Get starting point... 'nastier' 'k'
intend = Get end point... 'nastier' 'k'
intdur = intend - intstart
intmid = intstart + (intdur / 2)
intlab$ = int_label$
displayoff = intdur * 1.5
displaystart = intstart - displayoff
displayend = intend + displayoff
# Make a copy of the sound
selectObject: "Sound 'soundname$'"
Copy: "'soundname$'_rekt"
# Rectify the signal, the first step in AM demodulation
Formula: "abs(self)"
# Low pass the Rectified version, completing the AM Demodulation
Filter (pass Hann band): 0, 40, 20
Rename: "'soundname$'_flow"
# Now extract the individual flow channels and delete the original
select Sound 'soundname$'_flow
Extract all channels
select Sound 'soundname$'_flow
Remove
if naschannel = 1
select Sound 'soundname$'_flow_ch1
Rename: "nasal_flow"
select Sound 'soundname$'_flow_ch2
Rename: "oral_flow"
else
select Sound 'soundname$'_flow_ch1
Rename: "oral_flow"
select Sound 'soundname$'_flow_ch2
Rename: "nasal_flow"
endif
# Process the oral flow to get standard deviations
select Sound oral_flow
omax = Get maximum: displaystart,displayend, "Sinc70"
osd = Get standard deviation: 1, 0,0
halfosd = osd*0.01
# Process the nasal flow to get standard deviations
select Sound nasal_flow
nmax = Get maximum: displaystart,displayend, "Sinc70"
nsd = Get standard deviation: 1, 0,0
halfnsd = nsd*0.01
####
# Get %Nasalance
####
select Sound nasal_flow
dur = Get total duration
# Basically take nasalflow / (oralflow+nasalflow), and don't calculate if nasality is stupidly small
Create Sound from formula: "'soundname$'_nasalance", 1, 0, 'dur', 44100, "if (Sound_nasal_flow [col]) < 'halfnsd' then 0 else if (Sound_oral_flow [col]) < 'halfosd' then 0 else (Sound_nasal_flow [col])/((Sound_nasal_flow [col]) + (Sound_oral_flow [col])) endif endif"
# Percentages don't go higher than 1...
Formula: "if self > 1 then 1 else self endif"
###
# Draw Pretty Pictures
###
Line width: 1
# First paint the spectrogram for the flow chart
Select outer viewport: 0, 9.5, 0, 9
Erase all
Select outer viewport: 0, 9.5, 0, 3
select Sound 'soundname$'
noprogress To Spectrogram: 0.005, 5000, 0.005, 20, "Hamming (raised sine-squared)"
Paint: displaystart,displayend, 0, 0, 100, "yes", 50, 6, 0, "no"
select TextGrid 'soundname$'
Black
Draw: displaystart, displayend, "yes", "yes", "yes"
select Spectrogram 'soundname$'
# Make a second spectrogram for the scaled flows
Select outer viewport: 0, 9.5, 3, 6
Paint: displaystart,displayend, 0, 0, 100, "yes", 50, 6, 0, "no"
Line width: 1
select TextGrid 'soundname$'
Draw: displaystart, displayend, "yes", "yes", "yes"
select Spectrogram 'soundname$'
if shownasalance
# Make a third spectrogram for the %nasalance
Select outer viewport: 0, 9.5, 6, 9
Paint: displaystart,displayend, 0, 0, 100, "yes", 50, 6, 0, "no"
Line width: 1
select TextGrid 'soundname$'
Black
Draw: displaystart, displayend, "yes", "yes", "yes"
select Spectrogram 'soundname$'
endif
select Spectrogram 'soundname$'
Remove
Select outer viewport: 0, 9.5, 0, 3
Line width: 2
Red
select Sound oral_flow
omax = Get maximum: displaystart,displayend, "Sinc70"
omin = Get minimum: displaystart,displayend, "Sinc70"
select Sound nasal_flow
nmax = Get maximum: displaystart,displayend, "Sinc70"
nmin = Get minimum: displaystart,displayend, "Sinc70"
if omax > nmax
omax = omax + (0.1*omax)
else
omax = nmax + (0.1*nmax)
endif
if omin < nmin
omin = omin + (0.5*omin)
else
omin = nmin + (0.5*nmin)
endif
Select outer viewport: 0, 9.5, 0, 3
Draw inner box
omin = omin
select Sound oral_flow
Draw: displaystart,displayend, 'omin', 'omax', "no", "Curve"
Line width: 2
Lime
select Sound nasal_flow
Draw: displaystart,displayend, 'omin', 'omax', "no", "Curve"
One mark right: 0, "no", "yes", "yes", "Zero"
Text top: "no", "Nasal (Green) vs. Acoustic (Red) signal"
Line width: 2
select Sound 'soundname$'_nasalance
Select outer viewport: 0, 9.5, 3, 6
Cyan
Draw: displaystart,displayend, 0, 1, "yes", "Curve"
One mark left: 0.5, "no", "yes", "yes", "50%"
Text top: "no", "Percent Nasalance over time (where both oral and nasal > 0)"
Select outer viewport: 0, 9.5, 0, 9
Line width: 1
Blue
select Sound 'soundname$'
Select outer viewport: 0, 9.5, 6, 9
Text top: "no", "Waveforms (Acoustic on Top, Nasal bud on Bottom)"
Draw: displaystart,displayend, 0, 0, "yes", "Curve"
Select outer viewport: 0, 9.5, 0, 9
# Now save the file
Save as 300-dpi PNG file: "'directory$'_graphs/'soundname$'_'intlab$'.png"
# Clean up a bit
selectObject: "Sound nasal_flow"
plusObject: "Sound oral_flow"
Combine to stereo
Rename: "'soundname$'_flow_pascal"
selectObject: "Sound nasal_flow"
plusObject: "Sound oral_flow"
Remove
###
# Extract the data to a file
###
# Calculate how large each jump is based on duration of vowel
size = intdur / (tpnum-1)
for point from 1 to tpnum
if point = 1
timepoint = 'intstart'
elsif point = tpnum
timepoint = 'intend'
else
timepoint = intstart + (size * (point-1))
endif
selectObject: "Sound 'soundname$'_flow_pascal"
naspasc = Get value at time: 1, 'timepoint', "Nearest"
orpasc = Get value at time: 2, 'timepoint', "Nearest"
selectObject: "Sound 'soundname$'_nasalance"
percnas = Get value at time: 1, 'timepoint', "Nearest"
vwlpct = ((timepoint-intstart)/intdur)*100
result_row$ = "'soundname$'" + tab$ + "'intlab$'" + tab$ + "'point'" + tab$ + "'vwlpct:2'" + tab$ + "'naspasc:10'" + tab$ + "'orpasc:10'" + tab$ + "'percnas:5'" + tab$ + "'timepoint:4'" + newline$
fileappend "'resultfile$'" 'result_row$'
endfor
endif
endfor
else
selectObject: "Sound 'soundname$'"
Remove
endif
endfor
select Strings list
Remove
# Version History
# 1.0 - First release