-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpraat_vocoder.txt
3088 lines (2556 loc) · 108 KB
/
praat_vocoder.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
#############################################################################
#############################################################################
# Vocoder
# Vocode all selected sounds in the Objects list
# or all sounds in a specified folder
#
# Matthew Winn
#
# Version 59
# February 2024
#
#
#############################################################################
#############################################################################
# THE STARTUP WINDOW
#############################################################################
#############################################################################
form Enter vocoder settings
optionmenu analysis_frequency_allocation 1
option Cochlea-spaced based on these frequencies
option use ``Cochlear`` map
option use ``Med-El`` map
option use ``Advanced Bionics`` map
option use customized map from the script
option use custom linear channels
comment Note: anything other than option 1 will override the next four settings
comment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
comment overall upper and lower corner frequencies
real corner_frequency_low 100
real corner_frequency_high 8000
comment number of analysis & synthesis channels (n-of-m)
integer number_stimulated 12
integer number_of_channels 12
comment analysis filter slope (dB/oct) (leave at 0 for contiguous rectangular filters)
real analysis_filter_slope 0
comment synthesis filter parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
optionmenu carrier_type 1
option sinewave
option noise, rectangular
option noise, peaked (controlled filter rolloff/mm)
option noise, peaked (controlled filter rolloff/oct)
option low-noise noise
option harmonic_complex, rectangular
option harmonic_complex, (controlled filter rolloff/mm)
option harmonic_complex, (controlled filter rolloff/oct)
#option use original sound
real harmonic_complex_F0 200
real peaked_carrier_rolloff_(dB_per_oct_or_mm) 6
# basal shift (in mm of cochlear space)
real basal_shift_(mm) 0
comment Envelope modification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
real envelope_cutoff_filter_(Hz) 300
boolean envelope_compression_or_quantization 0
comment Output ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# output name suffix (leave blank for auto-suffix)
sentence output_name_suffix
# run all sounds in a whole folder?
boolean process_entire_folder 0
boolean preserve_components_for_inspection 0
endform
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
# #
### MAIN WRAPPER PROCESSES ###
# #
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
clearinfo
call set_startup_parameters
#-----------------------------------------------------------------#
# #
# if you are vocoding sounds already in the objects window... #
# #
#-----------------------------------------------------------------#
if process_entire_folder == 0
pause select all sounds to be used for this operation
num_selected_sounds = numberOfSelected ("Sound")
# assign object pointers to each of those sounds
for selected_sound_index from 1 to num_selected_sounds
sound'selected_sound_index' = selected("Sound",selected_sound_index)
endfor
if num_selected_sounds >= 14
print vocoding (no print window updates)
endif
call flag_component_overflow
# Loop through the sounds that were selected in the Objects list,
for this_sound_index from 1 to num_selected_sounds
select sound'this_sound_index'
name$ = selected$("Sound")
if num_selected_sounds < 14
print vocoding 'name$'... ('this_sound_index' of 'num_selected_sounds')
endif
#stopwatch
stopwatch
call vocode 'name$'
time_elapsed = stopwatch
if num_selected_sounds < 14
print ...... took 'time_elapsed:3' seconds'newline$'
endif
endfor
elsif process_entire_folder == 1
#-----------------------------------------#
# #
# If you're running a whole folder... #
# #
#-----------------------------------------#
Create Strings as file list: "fileList", "'orig_directory$'\*.wav"
num_files = Get number of strings
# Only proceed if there are any files to work with
print Processing 'num_files' files in the folder 'orig_directory$''newline$'
if num_files < 1
exit no files in this directory!
endif
num_selected_sounds = num_files
call flag_component_overflow
# Make the new folder
system mkdir 'orig_directory$'\'new_directory_name$'
# Loop through the file list
for file_index from 1 to num_files
select Strings fileList
filename$ = Get string: 'file_index'
Read from file: "'orig_directory$'\'filename$'"
name$ = selected$("Sound")
call vocode 'name$'
# save the sound
select Sound 'name$''vocoded_suffix$'
Save as WAV file... 'orig_directory$'\'new_directory_name$'\'name$''vocoded_suffix$'.wav
# remove the new sound
select Sound 'name$''vocoded_suffix$'
Remove
# remove the original sound
select Sound 'name$'
Remove
endfor
endif
# Print the parameters used
print 'newline$'
call print_corner_frequencies
call print_vocoder_settings
if process_entire_folder == 1
# Save info box to the same folder
call saveInfoWindow 'orig_directory$'\'new_directory_name$' 'new_directory_name$'_vocoder_info
select Strings fileList
Remove
endif
call save_component_parts
# DONE
print 'newline$'DONE!!!
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
# #
# SUB-PROCEDURES #
# #
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
procedure vocode name$
# The main procedure, which contains a bunch of subroutines
# Get Sound info
select Sound 'name$'
start = Get start time
end = Get end time
samplerate = Get sampling frequency
original_samplerate = Get sampling frequency
call optional_resample
#===============================================================#
# DIVIDE INTO ANALYSIS CHANNELS
call divide_into_analysis_channels 'name$'
#===============================================================#
# MAKE CHANNEL ENVELOPES
call create_envelopes 'name$' number_of_channels rolloff_per_x envelope
#===============================================================#
# PEAK-PICKING
if number_stimulated != number_of_channels
call peakpick_envelopes number_of_channels number_stimulated window_refresh_rate
endif
#===============================================================#
# MAKE THE CARRIERS
call make_carriers
#===============================================================#
# COMBINE THE CHANNELS
select Sound carrier_channel_1
Copy... temp_vocoded
if number_of_channels > 1
for chan_index from 2 to number_of_channels
Formula... self[col] + Sound_carrier_channel_'chan_index'[col]
endfor
endif
#===============================================================#
# CLEANUP
call cleanup_temp_objects
#===============================================================#
# LOOSE-ENDS: INTENSITY, EMPHASIS, RAMPING, CLIPPING, ETC.
call finalize_vocode_process
endproc
procedure divide_into_analysis_channels .sound$
#===============================================================#
# DIVIDE SOUND INTO ANALYSIS BANDS
# First, get some basic info
select Sound '.sound$'
.duration = Get total duration
.samplerate = Get sampling frequency
if pre_emphasize == 1
# first preemphasize
Filter (pre-emphasis): preemphasis_starting_frequency
Rename... '.sound$'
else
Copy... '.sound$'
endif
if analysis_channel_shape$ == "rectangular"
call divide_into_analysis_channels_rectangular '.sound$'
#yields: '.sound$'_analysis_channel_'.channel_loop_index' for all channels
elsif analysis_channel_shape$ == "sloped"
call divide_into_analysis_channels_sloped '.sound$'
#yields: '.sound$'_analysis_channel_'.channel_loop_index' for all channels
endif
# Remove the temporarily preemphasized sound
# because it shares a common name with the original sound object
# (even if you didn't preemphasize, it created this object)
select Sound '.sound$'
Remove
endproc
procedure create_envelopes .sound$ .numAnalysisChannels .rolloff_per_x .envelope_cutoff_freq
# This procedure creates sound objects
# that represent the envelopes of each channel.
# yields: channel_'.channel_loop_index'_ENV_step_3_LPF
# (because it is the output of a three-step process)
#
# First, get some basic info
select Sound '.sound$'
.duration = Get total duration
.samplerate = Get sampling frequency
#===============================================================#
# MAKE THE ENVELOPES
for .channel_loop_index from 1 to .numAnalysisChannels
# First get the original intensity
select Sound '.sound$'_analysis_channel_'.channel_loop_index'
channel_'.channel_loop_index'_intensity = Get intensity (dB)
#---------------------------------------------------#
# Ensure that the intensity is above 0
if channel_'.channel_loop_index'_intensity < 0
channel_'.channel_loop_index'_intensity = 0.01
endif
#===============================================================#
# Step 1: EXTRACT THE ENVELOPE
if use_Hilbert == 1
# Get HILBERT transform envelope
call do_Hilbert_transform '.sound$'_analysis_channel_'.channel_loop_index'
#-------------------------------------#
# envelope is "'.sound$'_analysis_channel_'.channel_loop_index'_ENV"
# TFS is "'.sound$'_analysis_channel_'.channel_loop_index'_TFS"
#-------------------------------------#
# remove TFS (we don't need that)
select Sound '.sound$'_analysis_channel_'.channel_loop_index'_TFS
Remove
else
# FAST ENVELOPE using Intensity Tier, etc
call fast_envelope '.sound$'_analysis_channel_'.channel_loop_index'
# envelope is "'.sound$'_analysis_channel_'.channel_loop_index'_ENV"
endif
if preserve_original_envelopes == 1
select Sound '.sound$'_analysis_channel_'.channel_loop_index'_ENV
Copy... env_ch_'.channel_loop_index'_orig
endif
#===============================================================#
# Step 2: COMPRESS the envelope
# if you selected anything other than "1", "0", or "-1" for the compression_mult
# work with the envelope_LPF Sound object
if compression_mult < 1
call compress_envelope '.sound$'_analysis_channel_'.channel_loop_index'_ENV compression_mult .envelope_cutoff_freq
# yields '.sound$'_analysis_channel_'.channel_loop_index'_ENV_compressed
select Sound '.sound$'_analysis_channel_'.channel_loop_index'_ENV_compressed
Rename... channel_'.channel_loop_index'_ENV_step_2
else
# Just rename it to match the expected naming scheme
select Sound '.sound$'_analysis_channel_'.channel_loop_index'_ENV
Rename... channel_'.channel_loop_index'_ENV_step_2
endif
if preserve_compressed_envelopes == 1
select Sound channel_'.channel_loop_index'_ENV_step_2
Copy... env_ch_'.channel_loop_index'_2_compressed
endif
#===============================================================#
# Step 3: LOW-PASS FILTER THE ENVELOPE
# if you've got a number above zero in the LPF argument,
# use it as the upper limit of a low-pass filter
if .envelope_cutoff_freq > 0
select Sound channel_'.channel_loop_index'_ENV_step_2
# rename for easy object removal later
Rename... full_envelope
# just extract the low-pass envelope (i.e. below 50 Hz)
Filter (pass Hann band): 0, .envelope_cutoff_freq, 2
Rename... channel_'.channel_loop_index'_ENV_step_3_LPF
# remove near-silence
if remove_near_silence
Formula: "if self[col] < silence_threshold then 0 else self endif"
endif
if preserve_LPF_envelopes == 1
select Sound channel_'.channel_loop_index'_ENV_step_3_LPF
Copy... env_ch_'.channel_loop_index'_3_compressed_LPF
endif
select Sound full_envelope
Remove
#===============================================================#
# Secret code to SCRAMBLE envelope phase
elsif .envelope_cutoff_freq == 0
# Secret code to SCRAMBLE envelope phase: cutoff frequency is 0
select Sound channel_'.channel_loop_index'_ENV_step_2
env_LPF = 50
keep_periodicity = 1
draw_env = 0
call modify_ENV channel_'.channel_loop_index'_ENV_step_2 env_LPF scramble keep_periodicity scramble_mix _scrambled draw_env
select Sound channel_'.channel_loop_index'_ENV_step_2_scrambled
Rename... channel_'.channel_loop_index'_ENV_step_3_LPF
#===============================================================#
# Secret code to INVERT the envelope: cutoff frequency is -1
elsif .envelope_cutoff_freq == -1
select Sound channel_'.channel_loop_index'_ENV_step_2
env_LPF = 50
keep_periodicity = 1
draw_env = 0
call modify_ENV channel_'.channel_loop_index'_ENV_step_2 env_LPF scramble keep_periodicity scramble_mix _inverted draw_env
select Sound channel_'.channel_loop_index'_ENV_step_2_inverted
#Rename... envelope_LPF
Rename... channel_'.channel_loop_index'_ENV_step_3_LPF
endif
endfor
endproc
procedure fast_envelope .name$
# Obtain the envelope of a sound
# This was written because doing an actual Hilbert transform
# can be very slow, and this procedure gives you the same results
# using fast native-Praat functions
#
# First, make the amplitude envelope
select Sound '.name$'
.samplerate = Get sampling frequency
.duration = Get total duration
To Intensity: 800, 0, "yes"
selectObject: "Intensity '.name$'"
Down to IntensityTier
To AmplitudeTier
# Turn it into a sound object
Down to TableOfReal
To Matrix
Transpose
To Sound (slice): 2
Rename... temp
# Fix timing (in case of extension of shortening) and samplerate
Scale times to: 0, .duration
Resample: .samplerate, 5
Rename: "'.name$'_ENV"
# Cleanup
select Intensity '.name$'
plus IntensityTier '.name$'
plus AmplitudeTier '.name$'
plus TableOfReal '.name$'
plus Matrix '.name$'
plus Matrix '.name$'_transposed
plus Sound temp
Remove
endproc
procedure peakpick_envelopes num_channels num_selected window_refresh_rate
#=============================================================================#
# CONCEPT:
# If you selected a number of synthesis channels that is smaller than #
# the number of analysis channels, it does a peak-picking algorithm #
# "ACE"-style processing: walk along each time bin, vocode, keep top N peaks. #
#=============================================================================#
# IMPLEMENTATION:
# Downsamples a sound envelope so that there is one value for each time bin.
# Organizes all the intensity values in each time bin into a table.
# At every time point, the channel intensities are sorted
# so you can extract each of the top n.
#
# yields
# "channel_'n'_ENV_peakpicked"
num_channels_dropped = num_channels - num_selected
selectObject: "Sound channel_1_ENV_step_3_LPF"
orig_samplerate = Get sampling frequency
# grab all the channel envelopes...
selectObject: "Sound channel_1_ENV_step_3_LPF"
for chan_index from 2 to num_channels
plusObject: "Sound channel_'chan_index'_ENV_step_3_LPF"
endfor
# Filter to anti-alias, then downsample
Filter (pass Hann band): 0, window_refresh_rate, 20
resamplerate = window_refresh_rate*2
# round it to a whole number
resamplerate = 'resamplerate:0'
Resample: resamplerate, 20
# correct to ensure envelope >= 0
for chan_index from 1 to num_channels
# eliminate values below 0
Formula: "if self[col] < 0 then 0 else self[col] endif"
endfor
select Sound channel_1_ENV_step_3_LPF_band_'resamplerate'
num_samples = Get number of samples
Create simple Matrix: "xy", num_channels, num_samples+1, "0"
num_rows = Get number of rows
num_cols = Get number of columns
# Channel loop
for row_index from 1 to num_rows
# set channel number in first column
Set value: row_index, 1, row_index
# time-sample loop
for col_index from 2 to num_cols
# set channel number
Set value: row_index, col_index, Sound_channel_'row_index'_ENV_step_3_LPF_band_'resamplerate'[col_index-1]
endfor
endfor
select Matrix xy
To TableOfReal
To Table: "junk"
select Matrix xy
plus TableOfReal xy
Remove
select Table xy
Remove column: "junk"
Set column label (index): 1, "channel"
for col_index from 2 to num_cols
col_sample_index = col_index-1
Set column label (index): col_index, "s'col_sample_index'"
endfor
# Loop through each envelope sample
for col_index from 2 to num_cols
sample_index = col_index - 1
select Table xy
# sort by intensity in this time window sample
Sort rows: "s'sample_index'"
# loop through dropped channels
for n_dropped from 1 to num_channels_dropped
# zero-out the dropped channel
Set numeric value: n_dropped, "s'sample_index'", 0
endfor
endfor
# Re-sort by channel
select Table xy
Sort rows: "channel"
# Remove channel column
Remove column: "channel"
Down to Matrix
# Create new sounds out of each peak-picked envelope
for chan_index from 1 to num_channels
select Matrix xy
To Sound (slice): chan_index
Override sampling frequency: resamplerate
Shift times to: "start time", 0
Formula... if self[col] > 0 then 1 else 0 endif
Rename... junk
Resample: orig_samplerate, 2
Rename: "channel_'chan_index'_ENV_step_3_LPF_peakpicked_downsampled_binary"
select Sound junk
Remove
endfor
# Multiply the original envlope with the peak-picked envelope
for chan_index from 1 to num_channels
# multiple binary peak-picked envelope
# with true envelope
select Sound channel_'chan_index'_ENV_step_3_LPF
Copy... channel_'chan_index'_ENV_peakpicked
Formula... self[col] * Sound_channel_'chan_index'_ENV_step_3_LPF_peakpicked_downsampled_binary[col]
endfor
# intercept to save peak picking matrix
if intercept_to_save_channel_matrix == 1
pause save peak-picked matrix xy
endif
# Cleanup of all things apart from peakpicked channel envelopes
select Matrix xy
plus Table xy
for n from 1 to num_channels
plus Sound channel_'n'_ENV_step_3_LPF_band
plus Sound channel_'n'_ENV_step_3_LPF_band_'resamplerate'
plus Sound channel_'n'_ENV_step_3_LPF_peakpicked_downsampled_binary
endfor
Remove
# Must re-name the envelopes now to match the expected naming structure
call rename_peakpicked_envelopes
# now ready to re-multiply peakpicked envelope and TFS
endproc
procedure rename_peakpicked_envelopes
# restore uniform naming scheme
# that was temporarily changed via the peak-picking procedure
for n from 1 to number_of_channels
select Sound channel_'n'_ENV_step_3_LPF
Remove
select Sound channel_'n'_ENV_peakpicked
Rename... channel_'n'_ENV_step_3_LPF
endfor
endproc
procedure make_carriers
# make the carriers that are filtered & modulated
#
# In the case that a single broadband carrier is used for all channels
# ( e.g. noise, harmonic complex),
# create that carrier here
if carrier_type$ = "noise"
if carrier_filter_shape$ != "LNN"
# create one white noise now, to be narrowband filtered later
# in procedure `make_LNN_ERB`
do ("Create Sound from formula...", "carrier", num_stereo_channels, 0, end, samplerate, "randomGauss(0,0.1)")
endif
elsif carrier_type$ == "harmonic_complex"
# make tone complex here
# to be narrowband filtered later
# however, this process might be sped up by not creating the *full* broadband sound;
# only generate the harmonics that are needed for this channel
# since the sound-generating process is a slow point
if variableExists ("highest_needed_output_freq_component") == 0
highest_needed_output_freq_component = 0
endif
Create Sound as tone complex: "carrier", 0, end, samplerate, "Sine", harmonic_complex_F0, 0, highest_needed_output_freq_component, 0
elsif carrier_type$ = "sinewave"
# ... wait until channel loop
elsif carrier_type$ = "LNN"
# ... wait until channel loop
elsif carrier_type$ == "original_sound"
# this will require a new per-channel process
# pause original-sound carrier not ready yet, as it requires per-channel filtering first
endif
#=======================================================#
# CREATE CARRIER CHANNELS FOR EACH FREQUENCY BAND
for .channel_loop_index from 1 to number_of_channels
#===============================================================#
# SINEWAVE CARRIER specific for this channel
if carrier_type$ == "sinewave"
cf = carrier_freq_center_'.channel_loop_index'
call make_sinewave cf end samplerate carrier
endif
#===============================================================#
# ORIGINAL SOUND AS CARRIER
# (currently not available in this version of the script)
if carrier_type$ == "original_sound"
select Sound 'name$'
Copy... carrier
endif
#===============================================================#
# LOW-NOISE NOISE CARRIER specific for this channel
# and do the envelope multiplication inline
if carrier_filter_shape$ = "LNN"
cf = carrier_freq_center_'.channel_loop_index'
call make_LNN_ERB cf end samplerate carrier
endif
#===============================================================#
# Multiply broadband carrier by the envelope
select Sound carrier
Copy... broad_carrier_channel_'.channel_loop_index'_w_ENV
Formula... self[col] * Sound_channel_'.channel_loop_index'_ENV_step_3_LPF[col]
#===============================================================#
# SPECTRALLY FILTER ENVELOPE-MODULATED CARRIER
# to match the desired bandwidth or filter slope
call filter_carrier_channel .channel_loop_index
# yields "carrier_channel_'.channel_loop_index'"
#===============================================================#
# CLEANUP
if carrier_type$ == "sinewave"
select Sound carrier
Remove
endif
if carrier_type$ == "original_sound"
select Sound carrier
Remove
endif
#===============================================================#
# INTENSITY QUANTIZATION
# Secret code to NOT do intensity quantization:
# set number of intensity steps to be zero.
# If it's not zero, proceed with this procedure...
if num_intensity_steps > 0
select Sound carrier_channel_'.channel_loop_index'
# quantized envelope LPF
int_quant_lpf = 100
call quantize_intensity carrier_channel_'.channel_loop_index' num_intensity_steps min_input_range max_input_range int_quant_lpf
select Sound carrier_channel_'.channel_loop_index'
# rename & update object name status
Rename... temp_to_delete
select Sound carrier_channel_'.channel_loop_index'_int_quant
Rename... carrier_channel_'.channel_loop_index'
select Sound temp_to_delete
Remove
endif
# end loop through channels
endfor
endproc
procedure makeContinuum .steps .low .high .prefix$ printvalues
for .thisStep from 1 to .steps
temp = (('.thisStep'-1)*('.high'-'.low')/('.steps'-1))+'.low'
'.prefix$''.thisStep' = temp
check = '.prefix$''.thisStep'
if printvalues = 1
print '.prefix$''.thisStep''tab$''check:2' 'newline$'
endif
endfor
endproc
procedure circle_shift .sound$ .shift .newname$
select Sound '.sound$'
.end_time = Get total duration
part_1 = Extract part: .shift, .end_time, "rectangular", 1, "no"
select Sound '.sound$'
part_2 = Extract part: 0, .shift, "rectangular", 1, "no"
selectObject: part_1, part_2
Concatenate
Rename... '.newname$'
selectObject: part_1, part_2
Remove
endproc
procedure circle_shift_blend .sound$ .shift .blend_time .newname$
select Sound '.sound$'
.end_time = Get total duration
part_1 = Extract part for overlap: .shift, .end_time, .blend_time
select Sound '.sound$'
part_2 = Extract part for overlap: 0, .shift, .blend_time
selectObject: part_1, part_2
Concatenate with overlap... .blend_time
Rename... '.newname$'
selectObject: part_1, part_2
Remove
endproc
procedure get_envelope_TFS_chunks .name$
# This is a procedure that extracts the envelope of a sound
# in chunks of 32768 samples
# to work within memory limits
# Get duration to be used later
select Sound '.name$'
.duration = Get total duration
.samplerate = Get sampling frequency
# divide into 32768-sample chunks
# to facilitate faster Hilbert transform
call divide_32768 '.name$'
# figure out how many chunks were created
num_chunks = divide_32768.chunks_analyzed
# Hilbert transform for each successive chunk
for sound_part from 1 to num_chunks
call get_envelope_TFS '.name$'_part_'sound_part'
endfor
# Sequence the TFSs back together into one sound
nocheck select junk
for sound_part from 1 to num_chunks
plus Sound '.name$'_part_'sound_part'_TFS
endfor
Override sampling frequency: .samplerate
Concatenate
Rename... junk
Extract part: 0, .duration, "rectangular", 1, "no"
Rename... '.name$'_TFS
select Sound junk
Remove
# Sequence the ENVs back together into one sound
nocheck select junk
for sound_part from 1 to num_chunks
plus Sound '.name$'_part_'sound_part'_ENV
endfor
Override sampling frequency: .samplerate
Concatenate
Rename... junk
Extract part: 0, .duration, "rectangular", 1, "no"
Rename... '.name$'_ENV
select Sound junk
Remove
# Cleanup
nocheck select junk
for sound_part from 1 to num_chunks
plus Sound '.name$'_part_'sound_part'
plus Sound '.name$'_part_'sound_part'_TFS
plus Sound '.name$'_part_'sound_part'_ENV
endfor
Remove
endproc
procedure divide_32768 .name$
# Divide a sound into sequential chunks
# each with no more than 32678 (i.e. 2^15) samples
# that can then each be processed separately.
# Otherwise, processing will slow down immensely.
select Sound '.name$'
.samplerate = Get sampling frequency
.duration = Get total duration
.num_samples = Get number of samples
.samples_collected = 0
.chunks_analyzed = 0
.sample_start = 0
while .samples_collected < .num_samples
.sample_end = .sample_start + 32768
.time_start = Get time from sample number: .sample_start
.time_end = Get time from sample number: .sample_end
selectObject: "Sound '.name$'"
Extract part: .time_start, .time_end, "rectangular", 1, "no"
.samples_collected = .samples_collected + 32768
.chunks_analyzed = .chunks_analyzed + 1
.sample_start = .sample_start + 32769
Rename... '.name$'_part_'.chunks_analyzed'
endwhile
endproc
procedure saveInfoWindow outputDirectory$ outputFileName$
# save all the contents of the info window
filedelete 'outputDirectory$'\'outputFileName$'.txt
fappendinfo 'outputDirectory$'\'outputFileName$'.txt
endproc
procedure calculate_LNN_ERB_edges cf
# filter it into a single ERB
erb_center = hertzToErb(cf)
erb_width = erb(cf)
.freq_lower = cf - (erb_width/2) + 5
.freq_upper = cf + (erb_width/2) - 5
endproc
procedure make_LNN_ERB cf .duration .samplerate .newname$
# Make low-noise noise# whose bandwidth is one ERB.
num_iterations = num_LNN_iterations
# Make a broadband noise
do ("Create Sound from formula...", "noise_to_filter", 1, 0, .duration, .samplerate, "randomGauss(0,0.1)")
# Filter it into a single ERB
erb_center = hertzToErb(cf)
erb_width = erb(cf)
.freq_lower = cf - (erb_width/2) + 5
.freq_upper = cf + (erb_width/2) - 5
# Note: lower edge increased by 5 Hz, upper edge decreased by 5 Hz
# to accommodate 10 Hz symmetrical bandwidth of Hann frequency filter
lnn_sidebandwidth = 10
target_intensity = 60
original_sound$ = "noise_to_filter"
# Initiate a starting sound
select Sound 'original_sound$'
Filter (pass Hann band): .freq_lower, .freq_upper, lnn_sidebandwidth
Rename... 'original_sound$'_LNN_0
Scale intensity... target_intensity
# Divide by envelope, repeat filtering
for n from 1 to num_LNN_iterations
m = n - 1
# use the most recent output of this process
# (if it's run #1, use the original copy,
# which was renamed to end in "_0")
target_sound$ = "'original_sound$'_LNN_'m'"
# Get the envelope
if use_Hilbert == 1
call get_envelope_TFS_chunks 'target_sound$'
select Sound 'target_sound$'_TFS
Remove
else
# Faster envelope extraction
call fast_envelope 'target_sound$'
endif
select Sound 'target_sound$'
Copy... temp
# Divide by its own envelope to flatten the envelope
Formula: "self[col] / Sound_'target_sound$'_ENV[col]"
# Filter out spectral splatter
Filter (pass Hann band): .freq_lower, .freq_upper, lnn_sidebandwidth
# Rename for the next iteration
Rename... 'original_sound$'_LNN_'n'
Scale intensity... target_intensity
# cleanup
select Sound temp
plus Sound 'target_sound$'_ENV
Remove
endfor
# Cleanup the intermediate objects created along the way
select Sound noise_to_filter
Remove
for n from 1 to num_LNN_iterations
index_to_remove = n - 1
select Sound 'original_sound$'_LNN_'index_to_remove'
Remove
endfor
# Rename new output sound
select Sound 'original_sound$'_LNN_'num_LNN_iterations'
Rename... '.newname$'
endproc
procedure make_sinewave .freq .duration .samplerate .newname$
# Wrapper formula for native praat function to look more transparent in the sequence
Create Sound from formula: "'.newname$'", 1, 0, .duration, .samplerate, "1/2 * sin(2*pi*.freq*x)"
endproc
procedure get_envelope_TFS .name$
# Low-pass envelope at 8000 Hz by default
.lpf = 8000
select Sound '.name$'
# 1: Time-domain to frequency-domain conversion (DFT)
spectrum = To Spectrum: "no"
Rename: "original"
# 2: Hilbert transform
spectrumHilbert = Copy: "hilbert"
Formula: "if row=1 then Spectrum_original[2,col] else -Spectrum_original[1,col] fi"
soundHilbert = To Sound
# 3: Obtain the ENV from the analytic signal
env = Copy: "'.name$'_ENV"
Formula: "sqrt(self^2 + Sound_'.name$'[]^2)"
# low-pass filtered version
if .lpf > 0
Rename... temp
Filter (pass Hann band)... 0 .lpf 10
Rename... '.name$'_ENV
select Sound temp
Remove
endif
# 4: Obtain the TFS (method 1: cosine of the angle of the analytic signal)
selectObject: soundHilbert
tfs = Copy: "'.name$'_TFS"
Formula: "cos(arctan2(self, Sound_'.name$'[]))"
# 5: cleanup
removeObject: spectrum, spectrumHilbert, soundHilbert
endproc
procedure modify_ENV .name$ env_LPF .method$ .keep_periodicity .scramble_mix .suffix$ .draw
# constants
env_sidebandwidth = 10
cleanup_comparison = 1
.clean_mix = 1 - .scramble_mix
# Note: input .draw = 1 will draw the envelopes to the praat picture window
selectObject: "Sound '.name$'"
# Get some basic info
rms = Get root-mean-square: 0, 0
envelope_intensity = Get intensity (dB)
.samplerate = Get sampling frequency
# obtain the periodicity envelope
# (everything above the LF envelope cutoff)