-
Notifications
You must be signed in to change notification settings - Fork 7
/
Make_Formant_Continuum_v46.txt
1853 lines (1508 loc) · 61 KB
/
Make_Formant_Continuum_v46.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
##################################################################
# GUI-based wizard for creating realistic vowel formant continua
# from modified natural speech
#
# version 46
#
# use with demo sounds "sit.wav" and "seat.wav"
#
# Matthew B. Winn
# November 2023
##################################################################
form Enter settings for Formant Continuum
comment Formant and pitch analysis
natural Enter_number_of_steps_in_the_formant_continuum 7
natural Number_of_formants 4
natural Maximum_formant_(Hz) 4300
natural minimum_pitch_in_analysis_(Hz) 75
natural maximum_pitch_in_analysis_(Hz) 250
# select which formants you want to modify
boolean modify_f1 1
boolean modify_f2 1
boolean modify_f3 1
#override bandwidths?
boolean Override_bandwidths 1
#Enter settings for frequency blending
real Crossover_frequency_to_restore_original_signal_(Hz) 4000
real Width_of_filter_crossover_(Hz) 500
comment File settings
real Enter_final_intensity_for_all_new_sounds_(dB) 70
#Enter directory path for the new files (a folder that already exists)
sentence Enter_parent_directory_that_already_exists C:\Users\mwinn\Desktop\Speech_continua
#Enter basic name for the folder of output files
sentence Basic_name_for_new_folder_for_continuum_files demo2
#Enter prefix for the filenames (to be suffixed by continuum step number)
sentence Enter_prefix_for_the_filenames Step_
endform
##############
call convert_names_from_form
call initialize_variables
call select2Sounds
call select_manipulation_portions
call adjust_comparison_sound
call select_segment precursor
call select_segment postcursor
call check_duration precursor overlap_duration
call check_duration postcursor overlap_duration
call record_durations
call adjust_pitch_contour "'base$'" minpitch maxpitch
call extract_lowfreq_portion "'base$'_PitchAdjusted" crossover_frequency filter_bandwidth
call extract_highfreq_portion "'base$'_PitchAdjusted" crossover_frequency filter_bandwidth
call make_source_residual "'base$'_PitchAdjusted" samplerate
call spectral_shape_residual 'base$'_PitchAdjusted_SOURCE
call check_source_residual
call match_intensity_contour "'base$'_PitchAdjusted_SOURCE" 'base$'_PitchAdjusted_LP_portion 1 'blank$'
call make_vocaltract_filter "'base$'" num_formants max_formant_frequency window_length
if comparison_choice != 2
# make a copy of the same formant grid to manually alter
call make_vocaltract_filter "'comparison$'_Matched" num_formants max_formant_frequency window_length
endif
call clean_up_formantgrid_points
call set_bandwidths
call make_point_vowels
call check_formant_grid 'base$'
call check_reference_grid
call recheck_formant_grids
call up_to_formant_grid "'base$'" no
call up_to_formant_grid "'comparison$'_Matched" no
call make_formantgrid_continuum
call source_filter_resynth
call make_source_filter_lowfreq_portions
#call reemphasize_HF_energy_after_SF
call match_lowfreq_intensities
call blend_highpass_portion
call blend_lowpass_portion
call retain_original_intensity_contours
call extract_manipulated_section
call concatenate_sections
call assemble_continuum_to_display
call draw_formant_tracks
call draw_spectra
call print_continuum_info
call print_formant_values
call clean_up_intermediate_objects
call initiate_save
call display_continuum
#
#
##
###
#####
########
#############
#####################
##################################
############################################ PROCEDURES
procedure make_point_vowels
# uses the "whitened" voice source and makes a set of vowels that should be
# easily recognizable as point vowels, and schwa.
# if the user hears the quality of the original vowel (from the base sound),
# then the source "whitening" did not work well.
select FormantGrid 'base$'
Copy... Blank
for this_formant from 1 to num_formants
call clear_formantgrid_points Blank 'this_formant' 0 base_duration
Remove bandwidth points between... 'this_formant' 0 base_duration
start_manipulation_'base$'
endfor
# make the vowels
call make_vowel Blank 'base$_PitchAdjusted_SOURCE Point_i 290 2350 2600 3500 70
call make_vowel Blank 'base$_PitchAdjusted_SOURCE Point_a 800 1150 2400 3400 70
call make_vowel Blank 'base$_PitchAdjusted_SOURCE Point_u 350 800 2300 3100 70
call make_vowel Blank 'base$_PitchAdjusted_SOURCE Neutral_schwa 500 1500 2500 3500 70
# put the vowels together
select Sound Point_i
plus Sound Point_a
plus Sound Point_u
plus Sound Neutral_schwa
Concatenate recoverably
select Sound chain
Rename... Point_vowels
select TextGrid chain
Rename... Point_vowels
select Sound Point_vowels
plus TextGrid Point_vowels
View & Edit
beginPause ("Check for good resynthesis")
comment ("Listen to these vowels to ensure good resynthesis.")
comment ("They should sound like /i/, /a/ /u/ and schwa.")
endPause ("Cancel", "OK", 2)
# close this window, if the user didn't already close it
nocheck editor TextGrid Point_vowels
nocheck Close
nocheck endeditor
endproc
procedure make_vowel .formantGrid$ .source$ .vowelName$ .f1 .f2 .f3 .f4 .intensity
# This generates a vowel with steady formants,
# using a pre-made source and filter
#
# it's just used to generate recognizable vowels
# so that you can check to see if the residual source
# is good enough to produce new vowels.
#
#
# use same F5 for all vowel inputs
.f5 = 4500
select FormantGrid '.formantGrid$'
Copy... '.vowelName$'
# populate the new FormantGrid with formant values (input arguments)
for .this_formant from 1 to num_formants
select FormantGrid '.vowelName$'
Add formant point... '.this_formant' 0 .f'.this_formant'
Add bandwidth point... '.this_formant' 0.1 (60+(10*'.this_formant'))
endfor
# filter the source
select Sound 'base$'_PitchAdjusted_SOURCE
plus FormantGrid '.vowelName$'
Filter
Rename... '.vowelName$'
# add high-frequency energy (from the original base sound) to the vowel
Formula... self [col] + Sound_'base$'_PitchAdjusted_HPportion [col]
# zero out everything around the manip portion
do ("Set part to zero...", 0, start_manipulation_'base$', "at nearest zero crossing")
do ("Set part to zero...", end_manipulation_'base$', base_duration,"at nearest zero crossing")
Scale intensity... '.intensity'
# cleanup
select FormantGrid '.vowelName$'
Remove
endproc
procedure initiate_save
beginPause ("Save files?")
comment ("Do you want to save the new sound files & continuum info?")
choice ("Save", 1)
option ("Yes, save")
option ("No, do not save")
endPause ("Cancel", "OK", 2)
if save = 1
call save_files
endif
endproc
procedure save_files
# save a text file that contains all the details of the script-running process
# as well as a list of files that were created.
#
call make_directories
call save_info_window "'parent_dir$'/'new_subfolder$'" 'output_filename$'
call save_sound_files
call make_file_list "'parent_dir$'/'new_subfolder$'/Stimuli/" 'listName$'
# save the formant tables
selectObject: "Table formants_long"
Save as comma-separated file: "'parent_dir$'/'new_subfolder$'/formant_table_long.csv"
selectObject: "Table formants_wide"
Save as comma-separated file: "'parent_dir$'/'new_subfolder$'/formant_table_wide.csv"
endproc
procedure save_sound_files
# Save the sound files that were used during the running of the script
# including the original sounds, new stimuli,
# voice source, and the high-pass portion
#
# first, adjust numeric name of the continuum step to permit
# easy alphabeticization of one- and two-digit numbers
# i.e. '2' becomes '02' so that 10 doesn't get ordered before 2.
for this_step from 1 to 'number_of_formant_steps'
if number_of_formant_steps > 9
if this_step < 10
tempStep$ = "0'this_step'"
else
tempStep$ = "'this_step'"
endif
else
tempStep$ = "'this_step'"
endif
select Sound 'basename$''this_step'
Rename... 'basename$''tempStep$'
call save_wav_file "'basename$''tempStep$'" 'parent_dir$'/'new_subfolder$'/Stimuli/
call save_formant_grid "FILTER_step_'this_step'" 'parent_dir$'/'new_subfolder$'/FormantGrids/
endfor
call save_wav_file "'comparison$'" 'parent_dir$'/'new_subfolder$'/Original_sounds
call save_wav_file "'base$'" 'parent_dir$'/'new_subfolder$'/Original_sounds
call save_wav_file "'base$'_Voice_Source" 'parent_dir$'/'new_subfolder$'/FormantGrids/
call save_wav_file "'base$'_Voice_Source" 'parent_dir$'/'new_subfolder$'/FormantGrids/
call save_wav_file "'base$'_PitchAdjusted_VLP_portion" 'parent_dir$'/'new_subfolder$'/FormantGrids/
call save_wav_file "'base$'_PitchAdjusted_HPportion" 'parent_dir$'/'new_subfolder$'/FormantGrids/
call save_wav_file precursor 'parent_dir$'/'new_subfolder$'/Original_sounds
call save_wav_file postcursor 'parent_dir$'/'new_subfolder$'/Original_sounds
endproc
procedure save_formant_grid .name$ .directory$
select FormantGrid '.name$'
Save as text file... '.directory$'/'.name$'.FormantGrid
endproc
procedure save_wav_file .name$ .directory$
# wrapper for save function that makes it easier to read
# when in a long list
select Sound '.name$'
Save as WAV file... '.directory$'/'.name$'.wav
endproc
procedure make_file_list .soundDir$ listName$
# list all the sound files in a directory,
# save it in a specified location
Create Strings as file list... 'listName$' '.soundDir$'
Save as raw text file... 'parent_dir$'/'new_subfolder$'/'listName$'.txt
select Strings 'listName$'
Remove
endproc
procedure save_info_window outputDirectory$ output_filename$
# save all the contents of the info window
filedelete 'outputDirectory$'/'output_filename$'.txt
fappendinfo 'outputDirectory$'/'output_filename$'.txt
endproc
procedure make_directories
# makes new directories - one as the main directory and one for the stimuli
#system mkdir 'parent_dir$'/'new_subfolder$'
createFolder: "'parent_dir$'/'new_subfolder$'"
#system mkdir 'parent_dir$'/'new_subfolder$'/Stimuli
createFolder: "'parent_dir$'/'new_subfolder$'/Stimuli"
#system mkdir 'parent_dir$'/'new_subfolder$'/Original_sounds
createFolder: "'parent_dir$'/'new_subfolder$'/Original_sounds"
#system mkdir 'parent_dir$'/'new_subfolder$'/FormantGrids
createFolder: "'parent_dir$'/'new_subfolder$'/FormantGrids"
pause directories made in 'parent_dir$'/'new_subfolder$'
endproc
procedure draw_formant_tracks
# make schematic "spectrograms" f the formant tracks
# so you can see how they change over time,
# across the whole continuum
Erase all
Select outer viewport... 0 6 0 5
Line width... line_width
for this_step from 1 to 'number_of_formant_steps'
# create a color gradient between blue & red, based on the step number
coolgradient = ('this_step'-1)/('number_of_formant_steps'-1)
r = coolgradient
g = 0.0
b = 1-coolgradient
Colour... {'r','g','b'}
select FormantGrid FILTER_step_'this_step'
Draw... (start_manipulation_'base$'-0.015) (end_manipulation_'base$'+0.015) 0 max_formant_frequencyDraw no yes lines
endfor
# Annotate the dotted lines (currently omitted because it creates a little clutter)
#One mark bottom... start_manipulation_'base$' no yes yes vowel start
#One mark bottom... end_manipulation_'base$' no yes yes vowel end
endproc
procedure draw_spectra
## draw gradient-colored smoothed spectra from each continuum step
# Select the area beneath the formant tracks area
do ("Select outer viewport...", 0, 6, 5, 9.5)
for this_step from 1 to number_of_formant_steps
# create a color gradient between blue & red, based on the step number
redproportion = ('this_step'-1)/('number_of_formant_steps'-1)
# create rgb blend (starts at blue, ends at red)
r = redproportion
g = 0.0
b = 1-redproportion
Colour... {'r','g','b'}
select Sound 'basename$''this_step'
.name$ = selected$("Sound")
tempDur = Get total duration
# extract only the manipulated portion
do ("Extract part...", precursor_duration, (tempDur - postcursor_duration), "rectangular", 1, "no")
To Spectrum... yes
# smooth the spectrum to simplify the image
Cepstral smoothing... 'smoothing'
Rename... '.name$'_part_smooth
select Spectrum '.name$'_part
Remove
select Spectrum '.name$'_part_smooth
Draw... draw_Hz_low draw_Hz_high draw_dB_low draw_dB_high yes
# cleanup
select Spectrum '.name$'_part_smooth
plus Sound '.name$'_part
Remove
endfor
# re-select the formant tracks drawing
Select outer viewport... 0 6 0 5
endproc
procedure assemble_continuum_to_display
# put them all into a single annotated sound so that they can be viewed & heard together
select Sound 'basename$'1
for this_step from 2 to 'number_of_formant_steps'
plus Sound 'basename$''this_step'
endfor
Concatenate recoverably
select Sound chain
Rename... 'basename$'Continuum
select TextGrid chain
Rename... 'basename$'Continuum
endproc
procedure display_continuum
# call it up to view,
# unless it's too many sounds for the screen to feasibly display
if number_of_formant_steps < 15
select Sound 'basename$'Continuum
plus TextGrid 'basename$'Continuum
do ("View & Edit")
else
pause Too many formant steps to show all at once
endif
endproc
procedure clean_up_intermediate_objects
# clean up all the remaining objects in the list
select IntensityTier lowpass_portion
#plus Sound precursor
#plus Sound postcursor
for n from 1 to 'number_of_formant_steps'
plus Sound Step_'n'_SF
plus Sound Step_'n'_SF_with_HPportion
plus Sound Step_'n'_middle
endfor
if comparison_choice=1
#plus Manipulation 'comparison$'
#plus DurationTier 'comparison$'
plus Sound 'comparison$'_Matched
plus Formant 'comparison$'_Matched
endif
#plus Sound 'base$'_PitchAdjusted_HPportion
plus Sound 'base$'_PitchAdjusted
#plus Sound 'base$'_PitchAdjusted_SOURCE
plus Formant 'base$'
Remove
select Sound 'base$'_PitchAdjusted_SOURCE
Rename... 'base$'_Voice_Source
endproc
procedure select_manipulation_portions
# have the user point & click on landmakrs
# that will be used to define the region of the sound
# that will be manipulated in the script.
# always select landmarks for the base sound
call select_manipulation_portion 'base$'
# select landmarks for the comparison sound IF you chose a comparison sound
if comparison_choice != 2
call select_manipulation_portion 'comparison$'
else
comparison$ = "'base$'"
endif
manipulation_duration = end_manipulation_'base$' - start_manipulation_'base$'
endproc
procedure check_duration .name$ .duration
# ensure that a segment is at least as long as that required for temporal overlap.
select Sound '.name$'
.tempdur = Get total duration
if .tempdur <= (.duration/2)
# sound must be expanded
beginPause ("Duration")
comment ("The duration of your '.name$' object is shorter than what is required")
comment ("for cross-fading (per your overlap_duration.)")
comment ("The segment must be greater than half the overlap_duration.")
comment ("You can start over and adjust the overlap_duration in the script (bottom)")
comment ("or choose one of the following options")
choice ("alter_segment", 1)
option ("Add extra silence to the '.name$' segment")
option ("Shorten the overlap duration")
endPause ("Cancel", "OK", 2)
if alter_segment = 1
target_duration = overlap_duration+0.0001
if .name$ == "precursor"
# if it's the precursor, put the silence before the sound
call add_onset_silence precursor target_duration
else
# if it's the postcursor, put the silence after the sound.
call add_offset_silence postcursor target_duration
endif
else
# increase the overlap duration to half the segment duration, minus a little bit
overlap_duration = (.tempdur*2)-0.00001
endif
endif
endproc
procedure record_durations
# establish variables to be used later in the script
select Sound precursor
precursor_duration = Get total duration
select Sound postcursor
postcursor_duration = Get total duration
endproc
procedure add_offset_silence .name$ .target_duration
select Sound '.name$'
.num_channels = Get number of channels
Copy... temp
select Sound '.name$'
Remove
do ("Create Sound from formula...", .name$, .num_channels, 0, .target_duration, samplerate, "0")
formula... self [col] + Sound_temp [col]
select Sound temp
Remove
endproc
procedure add_onset_silence .name$ .target_duration
select Sound '.name$'
.tempdur = Get total duration
.num_channels = Get number of channels
.dur_silence_to_add = .target_duration - .tempdur
do ("Create Sound from formula...", "bufferSilence", .num_channels, 0, .dur_silence_to_add, samplerate, "0")
select Sound '.name$'
Copy... temp
select Sound bufferSilence
plus Sound temp
Concatenate
select Sound bufferSilence
plus Sound temp
plus Sound '.name$'
Remove
select Sound chain
Rename... '.name$'
endproc
procedure concatenate_sections
# combine the precursor, manipulated sound, and postcursor
# and blend together with crossfading window
# first, ensure that the postcursor is the last item in the list,
# so that it will be appended at the end.
select Sound postcursor
Copy... temp_postcursor
for this_step from 1 to 'number_of_formant_steps'
select Sound precursor
plus Sound Step_'this_step'_middle
plus Sound temp_postcursor
if overlap_duration > 0
# Concatenate with cross-fade
Concatenate with overlap... overlap_duration
else
# If the overlap duration is 0, concatenate with no blending.
# if you accidentally set it to be a negative number,
# it is coerced to be zero here.
overlap_duration = 0
Concatenate
endif
Rename... 'basename$''this_step'
Scale intensity... 'final_intensity'
endfor
# remove temporary end-of-list postcursor sound object
select Sound temp_postcursor
Remove
endproc
procedure extract_manipulated_section
# extract only the manipulation portion (user-defined region)
# from the re-filtered sounds
for this_step from 1 to 'number_of_formant_steps'
select Sound Step_'this_step'_SF_with_HPportion_w_Vlowpass_portion
Extract part... start_manipulation_'base$' end_manipulation_'base$' rectangular 1 no
Rename... Step_'this_step'_middle
endfor
endproc
procedure retain_original_intensity_contours
# Each step in the continuum is matched to the intensity contour from the original base sound
# This step occurs before the manipulated portion is boxed out,
# because the user might use a different segment for the
# leading / trailing segment.
for this_step from 1 to 'number_of_formant_steps'
call match_intensity_contour "Step_'this_step'_SF_with_HPportion_w_Vlowpass_portion" 'base$' 1 'blank$'
endfor
endproc
procedure blend_highpass_portion
# add the high-frequency portion from the original base sound
# to the low-passed re-filtered continuum steps.
# this just adds naturalness and full spectral bandwidth.
for this_step from 1 to 'number_of_formant_steps'
select Sound Step_'this_step'_SF_LP
Formula... self [col] + Sound_'base$'_PitchAdjusted_HPportion [col]
Rename... Step_'this_step'_SF_with_HPportion
endfor
endproc
procedure blend_lowpass_portion
# add the very-low-frequency portion from the original base sound
# to the low-passed re-filtered continuum steps.
# this ensures that you will avoid any weird filtering problems
# that produce strange super-low frequency components
# in the output.
for this_step from 1 to 'number_of_formant_steps'
select Sound Step_'this_step'_SF_with_HPportion
# omit the very low frequencies
Filter (stop Hann band)... 0 vlp_cutoff vlp_filter_bandwidth
# add the original low frequencies
Formula... self [col] + Sound_'base$'_PitchAdjusted_VLP_portion [col]
Rename... Step_'this_step'_SF_with_HPportion_w_Vlowpass_portion
endfor
endproc
procedure spectral_shape_residual .name$
# to compensate for rolloff of spectrum level
# at high frequencies
# following source-filter decomposition
select Sound '.name$'
Rename: "temp"
Filter (pre-emphasis): 1500
Rename: "temp2"
Filter (pre-emphasis): 2500
Rename: "temp3"
Filter (pre-emphasis): 3500
Rename: .name$
# cleanup
select Sound temp
plus Sound temp2
plus Sound temp3
Remove
endproc
procedure reemphasize_HF_energy_after_SF
# to compensate for rolloff of spectrum level
# following source-filter recomposition
#
# this is removed from the processing chain,
# because instead of applying it to each new sound
# that was reconsituted from filtering the source with the formantgrid,
# this filtering is instead applied to the voice source itself
# in procedure `spectral_shape_residual`
# so the filtering is inherited by each subsequent stage of processing
#
# this was done to preserve the user's ability to filter that residual voice source
# with a simply formantgrid,
# without needing to apply a chain of preemphasis filters
#for this_step from 1 to 'number_of_formant_steps'
# select Sound Step_'this_step'_SF_LP
# Rename: "temp"
# Filter (pre-emphasis): 1500
# Rename: "temp2"
# Filter (pre-emphasis): 2500
# Rename: "temp3"
# Filter (pre-emphasis): 3500
# Rename: "Step_'this_step'_SF_LP"
# select Sound temp
# plus Sound temp2
# plus Sound temp3
# Remove
#endfor
endproc
procedure match_lowfreq_intensities
# Ensure that the manipulated portion of each continuum step
# has an intensity contour that matches that of the
# corresponding frequency region in the original base sound.
# extract just the vowel from the original LP portion
# extract the vowel for each step
# comapre the differences, counteract the difference
# intensity of spectral band from VLP frequency up to froquency crossover
select Sound 'base$'_PitchAdjusted_LP_portion
Extract part... start_manipulation_'base$' end_manipulation_'base$' rectangular 1 no
Rename: "vowel_orig_LP"
orig_v_LP_intensity = Get intensity (dB)
for this_step from 1 to 'number_of_formant_steps'
select Sound Step_'this_step'_SF_LP
Extract part... start_manipulation_'base$' end_manipulation_'base$' rectangular 1 no
Rename: "vowel_step_'this_step'_LP"
temp_intensity = Get intensity (dB)
intensity_diff = temp_intensity - orig_v_LP_intensity
# apply that correciton factor to the filtered sound (not just theMatt extracted vowel)
select Sound Step_'this_step'_SF_LP
Multiply... 10^(-intensity_diff/20)
#Scale intensity... 'original_LP_intensity'
endfor
endproc
procedure make_source_filter_lowfreq_portions
# "Make source-filter low-pass portions"
# Take the re-filtered sound,
# low-pass filter it so that only the frequency region chosen for manipulation
# is present in the re-filtered signal.
# Higher-frequency portions are restored from the original signal
# in a later procedure.
for this_step from 1 to 'number_of_formant_steps'
select Sound Step_'this_step'_SF
Filter (pass Hann band)... vlp_cutoff crossover_frequency filter_bandwidth
Rename... Step_'this_step'_SF_LP
#Scale intensity... 'original_LP_intensity'
endfor
endproc
procedure check_source_residual
# check for "whiteness" of voice source spectrum.
# this step might be removed, as it is effectively replaced by the
# "make_vowels" procedure.
# However, this is the user's chance to filter the signal,
# in case they want to deliberately change the spectral slope,
# or remove a spurious peak.
beginPause ("Check source")
comment ("For advanced users (optional): ")
comment ("Check the Source sound object for sound quality")
comment ("It should be devoid of any perceptible vowel quality.")
comment ("If you can clearly hear the original vowel, ")
comment ("consider switching which sound is the base / which is the comparison")
endPause ("Cancel", "OK, I'm done", 2, 2)
endproc
procedure source_filter_resynth
# filter the voice source by each step in the formant continuum.
for this_step from 1 to 'number_of_formant_steps'
select Sound 'base$'_PitchAdjusted_SOURCE
plus FormantGrid FILTER_step_'this_step'
Filter
Rename... Step_'this_step'_SF
endfor
endproc
procedure make_formantgrid_continuum
# create as many formantgrids as there are steps in the continuum,
# populate them with formant points
# so that they can be used to filter residual source sounds later.
# loop through the continuum steps
for this_formant_step to 'number_of_formant_steps'
# create the working filter object for this step
select Formant 'base$'
Down to FormantGrid
Rename... FILTER_step_'this_formant_step'
# alter the formantgrid
for this_formant from 1 to num_formants
# only alter it if it was selected to be altered
if f'this_formant'mod = 1
# First delete all the existing formant points for that formant
call clear_formantgrid_points "FILTER_step_'this_formant_step'" 'this_formant' 0 base_duration
# Next, insert the entire formant trajectory for that formant,
# based on the interpolation between endpoints at this continuum step.
call alter_formant_grid "FILTER_step_'this_formant_step'" "'base$'" "'comparison$'_Matched" this_formant number_of_formant_steps this_formant_step start_manipulation_'base$' end_manipulation_'base$' num_timesteps
# end conditional if this formant is being manipulated
endif
endfor
endfor
endproc
procedure alter_formant_grid .filter$ .base$ .comparison$ .formant .number_of_steps .this_step .start .end .num_timesteps
# Populate a formant row in a FormantGrid
# across the specified number of timepoints.
# First, delete all formant points in this formant row
select FormantGrid '.filter$'
Remove formant points between... '.formant' .start .end
for this_time_step to .num_timesteps
## convert 'this_time_step' to an actual time value by interpolation
.num_timestepsize = ('.end' - '.start')/('.num_timesteps'-1)
.timepoint = (.num_timestepsize*('this_time_step'-1)) + '.start'
# Get the formant value from the base sound
select Formant '.base$'
.formantA = Get value at time... '.formant' '.timepoint' Hertz Linear
# Get the formant value from the comparison sound
select Formant '.comparison$'
.formantB = Get value at time... '.formant' '.timepoint' Hertz Linear
# Proceed only if the formant at this timepoint is a valid number in BOTH
# the base & comparison sound.
if .formantA <> undefined && .formantB <> undefined
if bark = 0
## interpolate the formant value (linear) for this step at this timepoint
.formantStep = ('.formantB' - '.formantA')/('.number_of_steps' - 1)
.formantInterp = (.formantStep*('.this_step'-1)) + .formantA
else
## interpolate the formant value (Bark) for this step at this timepoint
call freq2bark .formantA
barkA = freq2bark.out
call freq2bark .formantB
barkB = freq2bark.out
barkStep = (barkB-barkA)/('.number_of_steps' - 1)
barkInterp = (barkStep*(.this_step-1))+barkA
call bark2freq (barkInterp)
# the formant value is the product of interpolation using the bark scale
.formantInterp = bark2freq.out
endif
# Add that formant value to the FormantGrid
select FormantGrid '.filter$'
Add formant point... '.formant' '.timepoint' '.formantInterp'
# end conditional if both formants are not undefined
# i.e. if the formants at this time point are not *both* readable
# in the base & comparison sound,
# then do not insert a point in the FormantGrid
endif
endfor
endproc
procedure up_to_formant_grid .formantGrid$ .remove_original
# Convert FormantGrid object to Formant object
# So that you can query values from it.
# the second argument in the procedure is
# if you want to remove the original FormantGrid from the list
select FormantGrid '.formantGrid$'
To Formant... 0.01 0.1
if .remove_original = 1
select FormantGrid '.formantGrid$'
Remove
endif
endproc
procedure recheck_formant_grids
pause (Optional) You can now take a moment to re-check any changes you made to the FormantGrids.
endproc
procedure check_formant_grid .sound$
# check & alter the FormantGrid and shape it into the contour that you want.
select FormantGrid '.sound$'
View & Edit
editor FormantGrid '.sound$'
Set formant range... 0 max_formant_frequency
Select... (start_manipulation_'base$'-0.015) (end_manipulation_'base$'+0.015)
Zoom to selection
# Pause window with some helpful tips
beginPause ("Alter the formant tracks")
comment ("Ensure that the formant tracks are smooth and continuous")
comment ("Delete any spurious points by clicking them (or highlighting a section)")
comment (" and press ctrl+alt+T")
comment ("Add a new formant point by pressing ctrl+T")
comment ("Switch from one formant to another by pressing ctrl+(formant number)")
comment (" (e.g. ctrl+2 for F2)")
comment (" ")
comment (" Note: if this is the base sound and you chose to *not* manipulate")
comment (" specific formants, then the contours you choose here will ")
comment (" be inherited by each continuum step.")
comment (" ")
comment (" Click OK when you are finished altering the FormantGrid")
endPause ("Cancel", "OK, I'm done", 2, 2)
nocheck editor FormantGrid '.sound$'
nocheck Close
endeditor
endproc
procedure check_reference_grid
if comparison_choice != 2
call check_formant_grid 'comparison$'_Matched
else
# If the user chose to simply make an alter-able copy of one sound,
# make a copy of that sound's FormantGrid,
# so that the user can work with two identical filters,
# and simply change the elements of interest.
select FormantGrid 'base$'
Copy... 'comparison$'_Matched
# now offer the user a chance to change that into the new opposite endpoint
call check_formant_gridCopy 'comparison$'_Matched
endif
endproc
procedure check_formant_gridCopy .sound$
select FormantGrid '.sound$'
View & Edit
editor FormantGrid '.sound$'
Set formant range... 0 max_formant_frequency
Select... (start_manipulation_'base$'-0.015) (end_manipulation_'base$'+0.015)
Zoom to selection
# pause window with helpful tips
beginPause ("Alter the formant tracks")
comment ("This is a copy of the FormantGrid that you just created")
comment ("You can now design a new formant contour")
comment ("for the opposite end of the continuum.")
comment ("Delete any formant points by clicking them (or highlighting a section)")
comment (" and press ctrl+alt+T")
comment ("Add a new formant point by pressing ctrl+T")
comment ("Switch from one formant to another by pressing ctrl+(formant number)")
comment (" (e.g. ctrl+2 for F2)")
comment (" ")
comment (" Click OK when you are finished altering the FormantGrid")
#comment (" Do not close the FormantGrid window.")
endPause ("Cancel", "OK, I'm done", 2, 2)
nocheck editor FormantGrid '.sound$'
nocheck Close
endeditor
endproc
procedure alter_formantgrid_bandwidths .sound$ .formant .endtime f1BW f2BW f3BW f4BW
# Override bandwidth tracking by inserting static formant bandwidth values
select FormantGrid '.sound$'
Remove bandwidth points between... '.formant' 0 .endtime
Add bandwidth point... .formant 0.1 f'.formant'BW
endproc
procedure clean_up_formantgrid_points
# Remove leading & trailing Formantgrid points
# So that the user knows exactly when the manipulation portion
# begins and ends.
# This is mostly aesthetic, as the portions affected by this step
# are not included after the middle-portion extraction step.
for this_formant from 1 to num_formants
call clear_formantgrid_points "'base$'" 'this_formant' 0 start_manipulation_'base$'
call clear_formantgrid_points "'base$'" 'this_formant' end_manipulation_'base$' base_duration
if comparison_choice != 2
call clear_formantgrid_points "'comparison$'_Matched" 'this_formant' 0 start_manipulation_'base$'
call clear_formantgrid_points "'comparison$'_Matched" 'this_formant' end_manipulation_'base$' ref_lengthened_duration
endif
endfor
endproc
procedure set_bandwidths
# Check the user settings for formant bandwidth adjustment.
# if bandwidth override was chosen, execute that procedure.
if override_bandwidths == 1
for this_formant from 1 to num_formants
call alter_formantgrid_bandwidths "'base$'" this_formant base_duration f1BW f2BW f3BW f4BW
endfor
endif
endproc
procedure clear_formantgrid_points .formantGrid$ .formant .start .end
select FormantGrid '.formantGrid$'
Remove formant points between... '.formant' .start .end
endproc
procedure make_vocaltract_filter .sound$ .num_formants .max_formant_frequency .window_length
# Make a formant object from the sound
# Convert it into a FormantGrid, which the user can customize.
# The resulting FormantGrid will *only* be as good as the Formant object,
# which is only as good as the settings used to generate it.
# Those settings are user-specified.
select Sound '.sound$'
To Formant (burg)... 0 .num_formants .max_formant_frequency .window_length 50
select Formant '.sound$'
Down to FormantGrid