-
Notifications
You must be signed in to change notification settings - Fork 2
/
Make_VOT_Continuum_v30.txt
1544 lines (1285 loc) · 53.2 KB
/
Make_VOT_Continuum_v30.txt
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
###########################################################################
###########################################################################
## Make VOT continuum
##
## Matthew B. Winn
## University of Minnesota
##
## Version 30
## August 13, 2019
##
## Three basic strategies:
## 1) Control VOT and F0 independently
##
## 2) Control VOT, have a constant level of F0:
## enter only 1 step of F0
## and type in that constant F0 level as the "min" F0
##
## 3) Co-vary VOT and F0 by having a different F0 for each step of VOT
## (This sounds most natural, and is intended for when
## you aren't interested in specific cue weighting,
## but are interested
## simply in the perception of voicing for demonstration)
##
## Inspect and modify some variables at the end of the script.
## ... such as controlling proportional VOT vowel cutback, etc.
##
## Options to shape VOT and vowel amplitude contours at segment boundary
## This is highly recommended. Otherwise, medium-lag sounds will sound goofy
## because there isn't a neat envelope transition from the burst
## to the vowel (this is especially noticable for /t/ sounds)
##
## You can prepare a pre-made aspiration sound
## - If you want to modify it in strange ways before making the continuum
## - ... or if you want to ensure that multiple different continua
## have the same exact VOT segments.
## - ... or if you want to do funny things like put the wrong aspiration on a segment
# ... or mix bursts and aspirations.
##
## 'new_output_folder' names the new sub-folder that will be created in the parent directory
## (the parent directory should be a folder that **already exists** on your computer)
##
## 'Sound_object_name_prefix' is a constant string prefix for each sound for output naming
##
## Note 1: This script is a mixture of different styles of Praat coding.
## Note 2: Requesting negative VOT values will initate creation of a "prevoicing" segment,
## ... which is not validated as a perfect match to natural prevoicing,
## ... but which I think sounds pretty good.
##
## Log of notable updates:
## Version 18: supports pre-voicing (preserves burst), warns against RMS equalization.
## Version 19: New startup window with different organization
## Version 26: New startup window with different organization,
## organized procedure files in order,
## check to make sure parent folder exists,
## and that the subfolders don't already exist
## changed some procedure names for transparency
## Check for adequate version of Praat
## Version 30: object selection by index instead of name
## saving functions changed to probably work across platforms
## without weird string combinations
## functions changed to accept object-index inputs
## rather than object names
## slight change in names for saved files
## Change in ordering of resampling procedures
## Saves original aspiration before lengthening adjustment
## Offers the option to clean up files
## if you're creating lots of them
## Easier-to-read end-of-script options
## for things like cutback ratio, forced extra control, etc.
## Better documentation overall
###########################################################################
###########################################################################
form Enter settings for VOT / F0 continuum
comment Enter VOT settings - number of steps, high (voiceless) & low (voiced) VOT endpoint values
natural number_of_VOT_steps 7
real left_vot_min_and_max 10
real right_vot_min_and_max 70
optionmenu Method_of_choosing_aspiration: 1
option Select VOT aspiration from original words
option Use a pre-made VOT aspiration segment
comment ------------------------------------------------------------------------------
comment Enter number of F0 steps, and the max & min F0 values (if only one step, enter same value)
natural f0_steps 5
real left_min_and_max_F0_onset -5
real right_min_and_max_F0_onset 40
optionmenu F0_manipulation: 1
option relative to original F0
option absolute Hz
optionmenu VOT_and_F0_covariance: 2
option Control VOT and F0 separately
option Covary F0 and VOT in tandem
real f0_time_range_in_ms 75
comment ------------------------------------------------------------------------------
boolean covary_aspiration_intensity 1
real aspiration_intensity_range 6
comment ------------------------------------------------------------------------------
optionmenu save_files: 2
option do not save any files
option save all the files and info
comment Enter directory path for the new files (a folder that already exists) (if you're saving output)
sentence parent_folder C:/Users/Matt/Desktop/Speech_continua
sentence new_output_folder VOT_DT
comment ------------------------------------------------------------------------------
sentence Sound_object_name_prefix DT
endform
#
#
##
###
#####
########
#############
#####################
##################################
#######################################################
## THE SEQUENCE OF ACTIONS #####################################
#######################################################
##################################
#####################
#############
########
#####
###
##
#
#
#======================================#
# Preparation
clearinfo
call check_praat_version
call set_variables_at_startup
#======================================#
# User-controlled actions
# Select the starting objects
# Next line hard-coded for DT demo;
# it won't cause an error if there's no DT_Deer sound.
nocheck select Sound DT_Deer
pause Select the voiced-onset sound
voiced$ = selected$ ("Sound")
voiced_sound_index = selected("Sound")
nocheck select Sound DT_Tier
pause Select the voiceless-onset sound
voiceless$ = selected$ ("Sound")
voiceless_sound_index = selected("Sound")
# Print to the info window
print ###############################################'newline$'
print SOUND SELECTION INFO'newline$'
print User selected 'voiced$' as the voiced endpoint'newline$'
print User selected 'voiceless$' as the voiceless endpoint'newline$'
print 'newline$'###############################################'newline$'
# Match sampling frequency to the highest of the two sounds
call match_samplerates_by_index voiced_sound_index voiceless_sound_index
# restore indexing variables
voiced_sound_index = match_samplerates_by_index.sound1
voiceless_sound_index = match_samplerates_by_index.sound2
# Select the aspiration segment that will be manipulated
# into a contiuum of aspiration durations
call select_aspiration_segment
# Mark the onset of the vowel in the voiced-onset sound
call mark_vowel_onset_ind 'voiced_sound_index'
#======================================#
# Automatic actions
# Set levels of VOT, F0, intensity, etc.
call generate_all_continuum_values
# Make the aspiration portions
call make_VOT_aspiration_sounds
# Make the vowel portions
call make_silent_buffer
call create_F0_manipulation_object
call make_VOT_cutbacks
# Put the pieces together and construct the continuum
call modify_and_assemble_components_and_save
# Save info files (does nothing if you didn't check the save box)
call save_info_files
# cleanup remaining files
call cleanup_remaining
# Assemble the continuum to view, if it's a small series
call assemble_continuum_series_to_view
pause Done!
## Finished :)
#
#
##
###
#####
########
#############
#####################
##################################
#######################################################
## P R O C E D U R E S ##################################################
#######################################################
##################################
#####################
#############
########
#####
###
##
#
#
procedure check_praat_version
#>> Ideally it's the most recent version...
#>> But I think that at least version 5.3.64 will do.
praat_version$ = left$(praatVersion$, (rindex(praatVersion$, ".")-1));
praat_version = 'praat_version$'
print Using Praat version 'praat_version$''newline$'
if praat_version < 6
exit Please download the most recent version of Praat.
endif
endproc
procedure check_if_parent_folder_exists parent_folder$
#>> If the user wants to save the files,
#>> Check if the user declared a valid parent folder to save them.
#>> It will do nothing if the directory doesn't exist
#>> (i.e. it won't write to a mysterious temp directory; it will throw an error,
#>> but the error won't be visible here because of the 'nocheck')
if save_files > 1
temp_filename$ = parent_folder$ + "/" + "my_temporary_Praat_file.txt"
# write a temporary txt file to the parent folder
nocheck writeFileLine: temp_filename$, "This is just to check if the directory exists"
# can the file be found?
file_exists = fileReadable (temp_filename$)
if file_exists == 1
# if you *could* read that file,
# then you can delete it,
# and this is confirmation that the directory is valid.
deleteFile: temp_filename$
print Saving to parent folder 'parent_folder$''newline$'
else
# if that file wasn't readable, that means that the directory wasn't valid.
print 'newline$'No filepath found
exit Your parent directory doesn't exist. Check spelling. The parent directory must *already* exist.
endif
endif
endproc
procedure check_if_subfolders_already_exist
#>> check if this sub-directory has *already* been made
#>> and give the user a chance to stop the script
#>> before it over-writes previously created files
# first see if there are already files in the "new" sub-folder
Create Strings as directory list: "directoryList", parent_folder$ + "/" + new_output_folder$
num_subfolders = Get number of strings
# If that folder already contains stuff,
# then stop and ask the user if they want to proceed
if num_subfolders > 0
sub_folder_names$ = ""
for index from 1 to num_subfolders
sub_folder_name$ = Get string: index
if index > 1
sub_folder_name$ = ", " + "'sub_folder_name$'"
endif
# add to the list of current sub-folder names
sub_folder_names$ = "'sub_folder_names$'" + "'sub_folder_name$'"
endfor
beginPause: "'parent_folder$' already includes sub-folder 'new_output_folder$' with 'sub_folder_names$'"
comment: "Proceeding might over-write files that already exist!"
comment: "How do you want to proceed?"
clicked = endPause: "Continue anyway (risky)", "Exit now", 0
if clicked == 2
# if user wants to exit, exit right away
select Strings directoryList
Remove
exit
endif
endif
# remove the object used to check if the sub-folders existed
select Strings directoryList
Remove
endproc
procedure make_save_directories
#>> Makes new directories
#>> one as the main directory
#>> one for the stimuli, and one for the original components
# Different method recommended by Paul Boersma
createDirectory (parent_folder$ + "/" + new_output_folder$)
createDirectory (parent_folder$ + "/" + new_output_folder$ + "/Stimuli")
createDirectory (parent_folder$ + "/" + new_output_folder$ + "/Original_sounds")
# Alert the user
new_folder_to_display$ = parent_folder$ + "/" + new_output_folder$
pause Directories made in 'new_folder_to_display$'
endproc
procedure select_aspiration_segment
#>> Asks the user to select the aspiration segment
#>> that will be used to create the continuum.
#>> Assigns the variable string name 'full_aspiration_sound$'
#>> to the full-duration segment
#>> either by selecting from an original sound,
#>> or by choosing an object in the Objects list
if preselected = 1
# You already have a prepared aspiration segment
pause Click on the sound to be used for the VOT burst & aspiration
# Whatever the name of the object is,
# that's now the value that shows up when you query 'full_aspiration_sound$'
full_aspiration_sound$ = selected$ ("Sound")
full_aspiration_sound = selected("Sound")
print used 'full_aspiration_sound$' for the aspiration noise'newline$'
elsif preselected = 0
# Select a segment from the voiceless-onset sound
call select_aspiration_from_original_ind 'voiceless_sound_index' vot_segment
# establish variables with the newly-created sound object
# from that procedure
full_aspiration_sound$ = selected$("Sound")
full_aspiration_sound = selected("Sound")
endif
# Get some info to be used later
raw_aspiration_duration = Get total duration
aspiration_intensity_orig = Get intensity (dB)
# If the length of the aspiration segment
# doesn't support the entire VOT continuum,
# it is lengthened and re-cross-faded;
# extensive lengthening (2.4x) forbidden.
# first initiate this variable for later
aspiration_modified = 0
# calculate how much you'd need to modify the aspiration
requiredDuration = vot_max + vot_crossfade_duration
if raw_aspiration_duration < requiredDuration
call lengthen_special_ind 'full_aspiration_sound' requiredDuration lengthened_VOT
# save the original aspiration for posterity
select full_aspiration_sound
if save_files > 1
Save as WAV file: "'parent_folder$'/'new_output_folder$'/Original_sounds/" + "original_aspiration_sound_before_lengthening.wav"
endif
# Re-establish variable name for the full aspiration
select Sound lengthened_VOT
full_aspiration_sound$ = selected$("Sound")
full_aspiration_sound = selected("Sound")
endif
endproc
procedure select_aspiration_from_original_ind .sound .newname$
#>> selecting the aspiration segment from among the original sounds
#>> you should have declared the first argument to be 'voiceless$'
select .sound
.sound$ = selected$("Sound")
View & Edit
editor Sound '.sound$'
# Mark aspiration ONSET
# Hard-coded for DT Deer-Tier example
Select... 0.05 0.05
pause Click on the START of the burst in the voiceless consonant
Move cursor to nearest zero crossing
aspiration_start = Get cursor
# Mark aspiration OFFSET
# Hard-coded for DT Deer-Tier example
Select... 0.121 0.121
pause Click on the END of the aspiration in the voiceless consonant
Move cursor to nearest zero crossing
aspiration_end = Get cursor
Close
endeditor
# Extract the aspiration
select .sound
Extract part: aspiration_start, aspiration_end, "rectangular", 1, "no"
Rename... '.newname$'
print ###############################################'newline$'
print VOT CONTINUUM INFO'newline$'
print 'newline$'###############################################'newline$'
print User selections'newline$'
print Selected an aspiration segment from 'voiceless$' sound'newline$'
print with onset landmark at 'aspiration_start''newline$'
print and offset landmark at 'aspiration_end''newline$'
endproc
procedure lengthen_special_ind .sound .required_duration .newname$
#>> "Lengthen" by simply taking the last third
#>> and appending it to the end of the original segment
#>> this is ugly, but sounds better in the case of working with aspiration segments,
#>> because the 'Lengthen' procedure produces spurious amplitude modulations.
# first check to see if it's feasible
select .sound
.name$ = selected$("Sound")
.orig_duration = Get total duration
.extra_needed = .required_duration - .orig_duration
.multiplier = .required_duration/.orig_duration
original_aspiration_sound = selected("Sound")
print 'newline$'###############################################'newline$'
if .multiplier > 2.4
# Not feasible to lengthen by a factor of more than 2.4;
# it would sound really choopy and unnatural
print required duration is '.required_duration:3' and your total sound duration is '.orig_duration:3' :( 'newline$'
print lengthening your segment by the appropriate amount ('.multiplier:3') will cause too much distortion.
exit Sorry, the chosen sound is not long enough to support the VOT continuum you want :( See Info Window for details.
elsif .extra_needed < (.orig_duration * 0.4)
# If you need only a little longer aspiration than you have,
# Simply append the last 0.4 (or less) to the original sound)
# instead of running the lengthen procedure
print Replicating the last '.extra_needed:3' at the end of the aspiration segment'newline$'
print to support the maximum length of the VOT continuum'newline$''newline$'
select .sound
# extract 30ms more than needed...
Extract part: (.orig_duration-.extra_needed)-0.03, .orig_duration, "rectangular", 1, "no"
Rename... temp_offset
# ... Blend it in smoothly with 30ms overlap
select .sound
plus Sound temp_offset
Concatenate with overlap... 0.03
Rename... junk
Extract part: 0, .required_duration, "rectangular", 1, "no"
Rename... '.newname$'
.new_sound = selected("Sound")
aspiration_modified = 1
select Sound junk
plus Sound temp_offset
Remove
elsif .extra_needed > (.orig_duration*0.4)
# if you need a substantially longer aspiration,
# run the lengthning procedure
print using regular Lengthen procedure 'newline$'
print using multiplier of '.multiplier:3''newline$'
select .sound
Lengthen (overlap-add)... 100 300 .multiplier
Rename... '.newname$'
.new_sound = selected("Sound")
aspiration_modified = 1
else
print No lengthening required for the aspiration segment to support maximum VOT in the continuum'newline$'
endif
select .new_sound
# VOT segment has been lengthened to accomodate your continuum
endproc
procedure mark_vowel_onset_ind .sound
#>> Asks the user to click on the vowel onset
#>> i.e. where the periodicity begins
select .sound
.sound$ = selected$("Sound")
View & Edit
editor Sound '.sound$'
# Hard-coded for DT Deer-Tier example
Select... (0.05416757235438554) (0.05416757235438554)
pause click on the vowel onset
Move cursor to nearest zero crossing
vowel_start_point = Get cursor
Close
endeditor
print 'newline$'###############################################'newline$'
print Marked 'vowel_start_point' as the vowel onset in the '.sound$' sound'newline$'
endproc
procedure match_samplerates_by_index .sound1 .sound2
#>> If two sounds have different sampling rates,
#>> up-sample the lower-sampled one.
select .sound1
.sound1$ = selected$("Sound")
.samplerate1 = Get sampling frequency
numchannels = Get number of channels
select .sound2
.sound2$ = selected$("Sound")
.samplerate2 = Get sampling frequency
if .samplerate1 > .samplerate2
samplerate = .samplerate1
select .sound2
resampled_sound2 = Resample... '.samplerate1' 50
# line to update info window
print 'newline$'Upsampled '.sound2$' to '.samplerate1' Hz to match '.sound1$''newline$'
select .sound2
Remove
select resampled_sound2
Rename... '.sound2$'
# restore indexing variable
.sound2 = selected("Sound")
elsif .samplerate2 > .samplerate1
samplerate = .samplerate2
select .sound1
resampled_sound1 = Resample... '.samplerate2' 50
# line to update info window
print 'newline$'Upsampled '.sound2$' to '.samplerate1' Hz to match '.sound1$''newline$'
select .sound1
Remove
select resampled_sound1
Rename... '.sound1$'
# restore indexing variable
.sound2 = selected("Sound")
else
samplerate = .samplerate1
endif
endproc
procedure generate_all_continuum_values
#>> Declare all the continuum values for VOT, F0, and aspiration intensity
#>> for use in the script, and for printing to the info window
# Print VOT continuum values
print 'newline$'
print 'newline$'###############################################'newline$'
print VOT steps 'newline$'
call generate_continuum_levels number_of_VOT_steps vot_min vot_max VOT_ 1 3
print 'newline$'
# yields variable levels with the name step_VOT_'n'
max_cutback_used = step_VOT_'number_of_VOT_steps' * vowel_cutback_to_VOT_ratio
# info for printing to the window:
extra_aspiration_added = vot_crossfade_duration/2
extra_aspiration_added = extra_aspiration_added * 1000
extra_aspiration_added = 'extra_aspiration_added:0'
print Added 'extra_VOT_silence_buffer' silence at the onset of each sound'newline$'
print Added 'extra_aspiration_added' ms aspiration at the end for blending into the vowel'newline$'
print 'newline$'
print Vowel-cutback information:'newline$'
print For each 1 ms of VOT, 'vowel_cutback_to_VOT_ratio' ms of vowel cutback'newline$'
print ('vowel_cutback_to_VOT_ratio' vowel-cutback ratio)'newline$'
print Maximum vowel cutback actually used: 'max_cutback_used:3''newline$'
print Maximum vowel cutback duration permitted: 'max_cutback_dur' seconds'newline$'
print 'newline$'
print 'newline$'###############################################'newline$'
# If you're doing relative F0,
# obtain first F0 point here,
# so that you can send the relative values
# into the generate_continuum values procedure
if f0_manipulation_style$ == "relative_change_from_original"
call check_F0_tracking
# yields variable 'f0_onset'
# Update the min and max values relative to this measurement
min_F0_onset = min_F0_onset + f0_onset
max_F0_onset = max_F0_onset + f0_onset
print Original sound F0 onset: 'f0_onset:1''newline$'
print F0 continuum minimum: 'min_F0_onset:1''newline$'
print F0 continuum maximum: 'max_F0_onset:1''newline$'
print Modification declared as relative change from the original'newline$'
elsif f0_manipulation_style$ == "absolute_Hz"
min_F0_onset = min_F0_onset
max_F0_onset = max_F0_onset
print Original sound F0 onset: 'f0_onset:1''newline$'
print F0 continuum minimum: 'min_F0_onset:1''newline$'
print F0 continuum maximum: 'max_F0_onset:1''newline$'
print Modification declared as explict declaration of F0 in 'f0_manipulation_style$''newline$'
endif
# Print F0 continuum values
print 'newline$'###############################################'newline$'
print F0 steps: 'newline$'
if number_of_f0_steps > 1
call generate_continuum_levels number_of_f0_steps min_F0_onset max_F0_onset F0_ 1 0
print 'newline$'
# yields variable levels with the name step_F0_'n'
else
step_F0_1 = min_F0_onset
call generate_continuum_levels number_of_f0_steps min_F0_onset min_F0_onset F0_ 1 0
print F0 onset:'tab$''min_F0_onset:0' 'newline$'
endif
# Pitch settings
print Pitch analysis between 'minpitch' and 'maxpitch' Hz'newline$'
# Print aspiration intensities
print 'newline$'###############################################'newline$'
print Aspiration intensities (dB)'newline$'
call generate_continuum_levels number_of_VOT_steps aspiration_intensity_orig-aspiration_intensity_range aspiration_intensity_orig aspiration_int_ 1 1
print 'newline$'
print Aspiration change (dB)'newline$'
call generate_continuum_levels number_of_VOT_steps -aspiration_intensity_range 0 aspiration_int_change_ 1 2
print 'newline$'
# Crossfade setting
print 'newline$'###############################################'newline$'
print Used 'vot_crossfade_duration' as the VOT cross-fade duration 'newline$'
# Intensity setting
print 'newline$'###############################################'newline$'
if scale_to_equal_RMS$ == "no"
print Output sounds maintained natural intensity-VOT relationship'newline$'
print (not equalized for RMS)'newline$'
else
print output sounds equalized for RMS intensity; not equalized for loudness'newline$'
output intensity: 'equal_RMS_intensity_target''newline$'
endif
if shape_vowel_envelope_onset == 1
print 'newline$'###############################################'newline$'
print Vowel onset intensitying ramping'newline$'
endproc
procedure generate_continuum_levels .steps .low .high .prefix$ .printvalues .precision
#>> Takes continuum endpoints and number of steps,
#>> and calculates the interpolated points.
#>> It also assigns an array of variable names
#>> (with user-set variable name prefix),
#>> Prints them to the info window with set decimal precision
# Loop through continuum indices
for thisStep from 1 to .steps
# This step has to be split into two steps
#
# 1. First calculate the interpolated value
temp = (('thisStep'-1)*('.high'-'.low')/('.steps'-1))+'.low'
# 2. Dynamically declare a variable name
# and assign the temp value
# e.g. step_VOT_3, step_VOT_4, step_VOT_5, etc.
step_'.prefix$''thisStep' = temp
.value = step_'.prefix$''thisStep'
if .printvalues = 1
# Prints values with n digits of precision
.value_print = round( .value * 10^.precision )/( 10^.precision )
print '.prefix$''thisStep''tab$''.value_print' 'newline$'
endif
endfor
endproc
procedure check_F0_tracking
#>> Ensure that F0 tracking works,
#>> because if you try to manipulate F0,
#>> the manipulation will only work for regions
#>> that were properly tracked.
# Where to extract the first F0 reading
f0_start_point = vowel_start_point + (vot_crossfade_duration/2)
# Set an original timing landmark
.temp_landmark = f0_start_point
select Sound 'voiced$'
To Pitch... 0 minpitch maxpitch
f0_onset = Get value at time... f0_start_point Hertz Linear
# If the F0 at that point is undefined,
# then scroll along the sound until you find a defined point
while f0_onset = undefined
f0_start_point = f0_start_point + 0.001
f0_onset = Get value at time... f0_start_point Hertz Linear
endwhile
# Keep track of the amount of time advancement needed to find a pitch value
time_elapsed_until_f0_detection = f0_start_point - .temp_landmark
# If the pitch can't be tracked within 25 ms of the user-selected landmark,
# the user must define the pitch pulse tracking
# in the manipulation object
if time_elapsed_until_f0_detection > 0.025
Edit
editor Manipulation 'voiced$'
beginPause ("Ensure all blue pulses are marked.")
comment ("(ctrl+p to add pulses where there are missing blue lines)")
comment ("")
comment ("If you don't know what to do,")
comment ("then consider working with a different sound recording")
comment ("that has better pitch tracking.")
endPause ("Okay, I'm done",1)
Close
endeditor
endif
select Pitch 'voiced$'
Remove
endproc
procedure make_VOT_aspiration_sounds
#>> Pulls out the appropriate duration of aspiration noise
#>> from the full-length aspiration
# First, make a little bit of silence to append and preappend to the cutback vowel
call make_silence extra_VOT_silence_buffer extra_VOT_silence_buffer 1
# Loop through continuum indices
for thisStep from 1 to number_of_VOT_steps
vot_duration_this_step = step_VOT_'thisStep'
if vot_duration_this_step > 0
# POSITIVE VOT
# If the VOT is at least zero,
# Takes the full aspiration segment and chops it
# to the length specified for this continuum step
# Sets VOT duration, plus extra blend time for cross-fading
vot_duration_this_step = step_VOT_'thisStep' + (vot_crossfade_duration/2)
call extract_VOT_segment_ind 'full_aspiration_sound' 'thisStep' 'vot_duration_this_step'
# Intensity adjustment (optional)
if covary_aspiration_intensity = 1
select Sound VOT_aspiration_'thisStep'
# dB adjustment is step_aspiration_int_change_'thisStep'
Multiply... 10^(step_aspiration_int_change_'thisStep' / 20)
endif
else
# PREVOICING
# If it's a negative VOT, make prevoicing
# first make the murmur
call make_prevoicing_ind -vot_duration_this_step 'voiced_sound_index' 'vowel_start_point' 'thisStep' prevoicing_extracted
# Prepare silent interval to introduce prevoicing
# Leave 3ms for them to overlap.
call make_silence buffer_to_preappend -(vot_duration_this_step-0.001) numchannels
# Extract just the burst (the first 5ms)
call extract_VOT_segment_ind 'full_aspiration_sound' 'thisStep' (0.005+(vot_crossfade_duration/2))
select Sound VOT_aspiration_'thisStep'
Rename... temp_burst_w_onset_buffer
temp_duration = Get total duration
# Remove the absolute buffer
Extract part... extra_VOT_silence_buffer temp_duration rectangular 1 no
Rename... temp_burst
# Prepare another buffer to re-introduce later
call make_silence buffer_to_restore extra_VOT_silence_buffer numchannels
# Burst intensity adjustment
# ... if you chose that option
if covary_aspiration_intensity == 1
if covary_even_for_prevoicing == 1
select Sound temp_burst
# dB adjustment is step_aspiration_int_change_'thisStep'
Multiply... 10^(step_aspiration_int_change_'thisStep' / 20)
endif
endif
# Add silence to the onset of the burst
# for when it's added to the prevoicing
select Sound buffer_to_preappend
plus Sound temp_burst
Concatenate
Formula... self[col] + Sound_prevoicing_extracted[col]
Rename... temp_burst_w_silent_buffer_for_prevoicing
# Re-introduce absolute onset silence buffer
select Sound buffer_to_restore
plus Sound temp_burst_w_silent_buffer_for_prevoicing
Concatenate
# Rename according to same style as for positive VOT
Rename... VOT_aspiration_'thisStep'
# Cleanup
select Sound prevoicing_extracted
plus Sound temp_burst_w_onset_buffer
plus Sound temp_burst
plus Sound buffer_to_preappend
plus Sound buffer_to_restore
plus Sound temp_burst_w_silent_buffer_for_prevoicing
Remove
endif
endfor
if extracontrol == 1
# User can inspect the aspiration continuum
pause Check vot continuum steps to make sure they all seem reasonable
endif
endproc
procedure make_silence .name$ .duration .numchannels
#>> Wrapper for make sound function
Create Sound from formula... '.name$' '.numchannels' 0 '.duration' 'samplerate' 0
endproc
procedure extract_VOT_segment_ind .sound .number .newduration
#>> Pulls out a segment from a longer sound
#>> and adds the silent buffer at the onset
#>> yields Sound obejct named VOT_'.number'
select .sound
extracted = Extract part... 0 '.newduration' rectangular 1 no
if extra_VOT_silence_buffer > 0
select Sound extra_VOT_silence_buffer
plus extracted
Concatenate
Rename... VOT_aspiration_'.number'
else
select extracted
Copy... VOT_aspiration_'.number'
endif
select extracted
Remove
endproc
procedure make_prevoicing_ind .prevoicing_dur .sound_to_extract_from .onset_point .f0_step_index .newname$
#>> Create a faux "prevoicing" segment
#>> to preappend to sounds whose VOT is negative.
#>> This just extracts the onset of the vowel,
#>> shapes the amplitude envelope,
#>> and low-pass filters so it sounds like a murmur.
#>> It also matches the F0 to the onset F0 of the vowel
#>> to which it will be attached.
# Extract with trianglar duration
select .sound_to_extract_from
Copy... temp_to_extract_from
To Manipulation: 0.01, 75, 600
Extract pitch tier
Remove points between: 0, 25
# add a single point where the F0 will be
# at the start of the vowel
temp_F0_value = step_F0_'.f0_step_index'
Add point: 0.01, temp_F0_value
selectObject: "Manipulation temp_to_extract_from"
plusObject: "PitchTier temp_to_extract_from"
Replace pitch tier
selectObject: "Manipulation temp_to_extract_from"
Get resynthesis (overlap-add)
Rename... temp_to_extract_from_pitch_matched
Extract part: .onset_point, .onset_point + .prevoicing_dur, "triangular", 1, "no"
Rename... junk
Filter (pass Hann band): 0, 500, 400
# Attenuate
Multiply... 0.4
Rename... '.newname$'
# cleanup
select Sound junk
plus Sound temp_to_extract_from
plus Manipulation temp_to_extract_from
plus PitchTier temp_to_extract_from
plus Sound temp_to_extract_from_pitch_matched
Remove
endproc
procedure make_silent_buffer
#>> Creates silence to buffer onset & offset,
#>> where F0 mis-estimations can happen
Create Sound from formula: "buffer", 1, 0, 1, samplerate, "0"
endproc
procedure create_F0_manipulation_object
#>> Wrapper for creating the object that you can manipulate
#>> in terms of duration & F0
select Sound 'voiced$'
To Manipulation... 0.01 minpitch maxpitch
endproc
procedure make_VOT_cutbacks
#>> Takes the vowel, and progressively cuts away the onset,
#>> which will be replaced by the aspiration noise.
# Loop through the continuum indices
for vot_step_index from 1 to number_of_VOT_steps
# Set burst duration;
# This allows up to a certain VOT without any cutback at all.
minimum_burst_dur = 0.008
vot_relative_to_burst = step_VOT_'vot_step_index' - minimum_burst_dur
# Calculate cutback duration
# as a product of the VOT and the cutback ratio
cutback_dur_'vot_step_index' = vot_relative_to_burst * vowel_cutback_to_VOT_ratio
# If the cutback duration exceeds the max allowed,
# just set it to be the max allowed
if cutback_dur_'vot_step_index' > max_cutback_dur
cutback_dur_'vot_step_index' = max_cutback_dur
endif
# If this step is prevoiced,
# set cutback to be zero
if step_VOT_'vot_step_index' < minimum_burst_dur
cutback_dur_'vot_step_index' = 0
endif
# Set the point where the vowel begins
# (i.e. the cutback point)
# Relative to vowel onset that the user selected.
select Sound 'voiced$'
.dur = Get total duration
cutpoint_start = vowel_start_point + cutback_dur_'vot_step_index' - (vot_crossfade_duration/2)
# Extract the vowel cutback portion for this step
Extract part: cutpoint_start, .dur, "rectangular", 1, "no"
Rename... vowel_cutback_'vot_step_index'
endfor
endproc
procedure modify_and_assemble_components_and_save
#>> this is the function that loops through and performs
#>> all the sequential actions needed to build the continuum,
#>> including F0 manipulation,
#>> and concatenation of aspiration & vowel.
# Loop through the continuum indices
for vot_step_index from 1 to number_of_VOT_steps
#----------------------------------------------------#
# If you chose to covary VOT and F0,
# then set the F0 step to be the same as the VOT step,
# and only run a single F0 step (not a whole loop of them
# Note how there is no for-loop started in this part,
# unlike for the following section)
if covary = 1
current_F0_value = step_F0_'vot_step_index'
current_F0_step = 'vot_step_index'
call manipulate_vowel_f0_onset
call put_all_pieces_together_and_save
# Cleanup for each step of the VOT continuum
selectObject: "Manipulation temp_cutback_'vot_step_index'"
plusObject: "PitchTier temp_cutback_'vot_step_index'"
plusObject: "Sound temp_cutback_'vot_step_index'"
plusObject: "Sound vowel_cutback_'vot_step_index'"
Remove
elsif covary = 0
# If you are setting VOT and F0 independently
# (in a crossed-cue matrix of sounds),
# begin a new nested loop through all the F0 steps here