-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathvocode_all_selected.txt
2932 lines (2455 loc) · 97.7 KB
/
vocode_all_selected.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 47
# August 2022
#
#---------------------------------------------------------------------------
# Brief description: flexible vocoder that can be customized for specific:
# lower and upper corner frequencies, number of analysis and synthesis channels,
# an option for synthesis peak-picking (as in n-of-m / ACE cochlear implant processing),
# user-specified filter width to simulate spread of cochlear excitation,
# user-specified cochlear shift to simulate incomplete CI insertion,
# support for customized channel-frequency allocation,
# and options for various carrier types,
# including noise, sinewaves, harmonic complexes, and low-noise noise.
# Options for envelope compression and intensity quantization.
# Batch processing supported for any (reasonable) number of Sound objects
# currently open/selected in the Praat objects list.
#---------------------------------------------------------------------------
# DISCLAIMER: I'm an audiolgist, not an engineer :)
#############################################################################
# INSTRUCTIONS
# You fill out the vocoder parameters in the pop-up menu,
# and it will ask you to select all the sound objects in the list
# that you want to vocode.
# so, call up the sounds before you run the script.
#
# when you've entered your settings, hit Apply or OK
# it will then ask you to select all the sound objects in the window
# that you want to vocode.
# It will batch-process all your selections;
# note that some styles of vocoding are computationally intense,
# so you will want to experiment with 1 or 2 short sounds
# before really diving in with a full list.
#############################################################################
# Set upper and lower frequencies for analysis filter
#----------------------------------------------------------------------------
# Set number of analysis (numberOfChannels) & synthesis (numberStimulated) filters
# (for a conventional vocoder, these will be the same number)
# e.g. you might type in 8 and 8 for a typical 8-channel vocoder.
# if you choose a lower number for the synthesis filter, the script
# will create a peak-picking vocoder that emulates the
# advanced combination encoder (ACE)-style of peak-picking
# in successive time bins.
# e.g. 22 possible channels, but only 8 stimulated at any point in time.
# NOTE: if you choose this option, the script will run
# more slowly than if it's a conventional n-channel vocoder
#----------------------------------------------------------------------------
# Choose a CARRIER TYPE: noise, sinewave, or tone complex
# Noise is bands of filtered white noise whose bandwidth fills the frequency channel
# Sinewaves are at the center of the frequency channels
# Tone complexes are harmonic tones that are broadband and have a pulsatile rate
# equal to the F0 of the harmonics
#
# pulse-spreading harmonics will be enabled in the future,
# but are not currently supported.
# Pulsatile harmonic carrier is a wideband tone complex
# whose partial phase relationships produce a pulsatile signal.
# pulse rate equals F0 of harmonic complex
# (Hilkhuysen/Macherey-style high rate pulse-spreading harmonic complex
# is in the works and will be available in the next major version)
#----------------------------------------------------------------------------
# SHAPE OF THE CARRIER
# Choose the shape of the synthesis filter as "peaked"
# to model the spread of excitation of a cochlear implant electrode
# (controlled by the 'rolloff.per.mm' variable,
# which operates in terms of cochlear space)
# OR choose the "square" option, which is the conventional style,
# where a flat-spectrum noise is divided up into frequency bands,
# and the rolloff.per.mm is ignored
# if you select LNN (low-noise noise),
# then the shape will be flat,
# and the bandwidth will be just slightly narrower than 1 ERB
# If you choose sinewave carrier,
# then the shape variable does nothing; it's just a sinewave
#----------------------------------------------------------------------------
# ROLLOFF PER MM
# Choose the width of the synthesis filter in dB per mm
# anything below 6 will be quite distorted, while
# anything above 20 will usually sound pretty clear.
# super-high numbers (> 90) will sound more like distorted sinewave vocoders
# ** NOTE: This option will only be implemented if you selected "peaked"
# for your synthesis filter shape. **
#----------------------------------------------------------------------------
# TEMPORAL ENVELOPE CUTOFF FILTER
# to control temporal precision of the envelope
# If you set the envelope cutoff frequency to 0,
# then it is a secret code to scramble the envelope phases,
# but maintains the same envelope modulation spectrum and periodicity
# If you set this to happen,
# then you control the mixture of scrambled envelope to clean un-disturbed envelope
# otherwise, the "scramble_mix" parameter does nothing.
#----------------------------------------------------------------------------
# USE HILBERT
# there are two ways of extracting the envelope.
# one is the commonly cited Hilbert Transform,
# which is computationally demanding,
# as it requires a custom for-loop procedure in praat
# The other way is with a native praat IntensityTier / AmplitudeTier function
# Written in C++ and is blazing fast.
# I have verified that both give the same result as long as the signal isn't silent
# So I recommend leaving this box unchecked
# unless you NEED to say that you used the Hilbert Transform.
#----------------------------------------------------------------------------#
# SCRAMBLE MIX
# Don't worry about this. Leave it at 0.
# It's for when you want to mix in a distorted envelope
#----------------------------------------------------------------------------#
# NUMBER OF INTENSITY STEPS:
# QUANTIZATION OF THE ENVELOPE
# if you select num_intensity_steps = 0,
# then the envelope is fully preserved
# if you set num_intensity_steps to anything above 0,
# then you control a discrete number of intensity steps,
# ranging between the minimum and maximum intensity,
# and also including zero, for anything below the minimum input intensity range
# which is set in the bottom of the script in the fixed parameters
# Note: you'll probably only notice this if you use a steady-envelope carrier
# like sinewave, tone complex or LNN.
#----------------------------------------------------------------------------#
# COMPRESSION
# What proportion of modulation depth will remain after vocoding?
# If you select 1, then this is the typical style, where the envelope is preserved.
# If you select 0.5, then an amplitude modulation of X dB will change to X/2 dB
# If you select 0.25, then an amplitude modulation of X dB will change to 0.25 * X dB
#----------------------------------------------------------------------------
# BASAL SHIFT
# To simulate incomplete array insertion depth
# Choose a length (in mm) of cochlear frequency shifting
# to simulate incomplete insertion of the array
# (higher numbers mean a greater upward shift in frequency;
# shifts of 3mm and above generally make speech very difficult to understand.
# NOTE: This doesn't account for actual electrode position,
# which does not correspond neatly to the spacing
# between characteristic places of the channel frequencies.
# It also doesn't shift according to insertion angle,
# as would be the case in a real CI.
# that feature might be installed in a later version of this script.
# In general, this vocoder shifts according to basilar membrane space,
# even though real cochlear implants stimulate at the spiral ganglion,
# NOT the baslilar membrane.
# So, we know this is an incorrect tonotopy,
# And also know that it is an imprecise shifting.
# It might not be entirely unrealistic,
# but we know that it is imprecise.
# For more details, see Stakhovskaya et al. (2007)
#----------------------------------------------------------------------------
# TIME BINS
# If you're doing the ACE-style peak-picking,
# select length of the time bins over which peaks are picked.
# (the conventional vocoder isn't affected by this, since the envelope
# is carried for each channel at all times, whereas for the peak-picking style,
# the channels stimulated depends on the exact part of the signal being analyzed.
# if you select 30 MS,
# It means that for every successive chunk of 30ms,
# the spectrum is analyzed to pick the top n out of m channels
# Note: the envelope is still preserved;
# this is just for how often you update the peak-picking.
#----------------------------------------------------------------------------
# BATCH PROCESSING
#
# If you want to run a whole folder of sounds, check this box,
# fill in the directory where the sounds *currently* reside,
# and the name of a new sub-folder that will contain the new vocoded sounds.
#
# If you want to preserve the same exact filenames on output as you had on input,
# then check the "remove suffix" box.
# This is useful when you have a stimulus list that gets deployed
# no matter what the condition being tested (as in my lab)
# Don't worry - the vocoder sounds won't over-write your current sounds,
# as they will be saved into a new sub-folder,
# along with the details of all your parameters that you used on startup.
# If you do NOT check this box,
# then all your new saved sounds will have a very long filename suffix
# indicating many (but nto all) the parameters that were used.
#----------------------------------------------------------------------------
# CUSTOM AND PRE-LOADED FREQUENCY-ALLOCATION TABLES
# You can also simulate the frequency allocation table used by various
# cochlear implant manufacturers, although there are limitations.
# These are just frequency tables, and it is not an indication
# that Praat is handling the sound in the same way that a CI processor does.
# In fact, we KNOW that it is different. Praat uses FFT filters,
# but not all the CI processors do this.
# so this is just a rough approximation of the filter channels,
# and does not simulate the placement of the device in the ear.
#
# If you select one of the custom frequency table options,
# your custom settings in the script will override your startup window settings.
#---------------------------------------------------------------------------
# A module has been added to simulate partial tripolar (PTP) stimulation
# as found in the Advanced Bionics device.
# Note that NO attempt is made to replicate the actual processing for the AB device;
# I'm just replicating the channel-frequency allocations and letting you choose
# your desired settings for spread of excitation (rolloff / mm).
#----------------------------------------------------------------------------
# If you want to simulate a specific custom frequency table,
# you can choose that option at the bottom of the pop-up menu,
# and it will use the channels defined at the bottom of this script.
# It's currently set up to simulate a 15-channel cochlear implant
#---------------------------------------------------------------------------
# By selecting the custom frequency table option,
# your custom settings in the script will override your startup window settings.
# so if you choose the custom table, your input settings don't matter,
# so don't bother typing them in.
#----------------------------------------------------------------------------
# Update version 47: Updated various things for speed,
# and updated the style of code from camelCase to snake_case.
# Also changed default filter side bandwidth to 20 instead of 50 Hz.
# but note that if you have a large number of channels,
# then your channel widths will be narrower than the filter,
# so that will cause some weird filtering. a
#############################################################################
#
#
#
#
#############################################################################
#############################################################################
# THE STARTUP WINDOW
#############################################################################
#############################################################################
form Enter vocoder settings
optionmenu filter 1
option Channels based on these corner frequencies
option use "Cochlear" map
option use "Med-El" map
option use "Advanced Bionics" map
option use "AB PTP" map
option use customized map from the script
comment overall upper and lower corner frequencies
real lowCornerFreq 100
real highCornerFreq 8000
comment number of analysis & synthesis channels (n-of-m)
integer numberStimulated 12
integer numberOfChannels 12
comment synthesis filter parameters
optionmenu carrier_type 1
option use noise carrier
option use pulsed carrier
option use sinewave
option use PSHC
option use original sound
#option use TFS pulse carrier
real pulseRate 200
optionmenu shape: 2
option peaked (controlled filter rolloff)
option square
option LNN
option LNN_premade
real rolloff.per.mm 6
real envelope_cutoff_filter_(Hz) 600
boolean use_Hilbert 0
real scramble_mix 0
integer num_intensity_steps 0
real compression_mult 1
# basal shift (in mm of cochlear space)
real basal_shift_(mm) 0
#comment time bins for peak-picking option
real time_bins_for_peak_picking_(ms) 30
# run all sounds in a whole folder?
boolean run_folder 0
sentence orig_directory L:\PraatScripts\Vocoder\Orig_sounds_demo
sentence new_directory_name Voc_8_ch_shift_3
boolean remove_suffix 0
endform
#############################################################################
#############################################################################
### PROCESSES
#############################################################################
#############################################################################
clearinfo
call setParameters
# IF YOU'RE VOCODING SOUNDS ALREADY IN THE OBJECT WINDOW...
if run_folder == 0
nocheck select Sound see
pause select all sounds to be used for this operation
numberOfSelectedSounds = numberOfSelected ("Sound")
for thisSelectedSound from 1 to numberOfSelectedSounds
sound'thisSelectedSound' = selected("Sound",thisSelectedSound)
endfor
# CYCLE THROUGH THE SOUNDS THAT WERE SELECTED
# AND VOCODE THEM, REPORTING THE PROCESSING TIME
for thisSound from 1 to numberOfSelectedSounds
# copy that index to use for extracting numbers later
file_index = thisSound
select sound'thisSound'
name$ = selected$("Sound")
print vocoding 'name$'... ('thisSound' of 'numberOfSelectedSounds') 'newline$'
# As long as you're not doing the interleaved version, proceed normally
if keep_these_channels$ != "interleaved"
stopwatch
call vocode 'name$'
time_elapsed = stopwatch
print ...... took 'time_elapsed:3' seconds'newline$'
else
call vocode_interleaved
endif
endfor
else
# 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
# 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$''vocodedSuffix$'
Save as WAV file... 'orig_directory$'\'new_directory_name$'\'name$''vocodedSuffix$'.wav
# remove the new sound
select Sound 'name$''vocodedSuffix$'
Remove
# remove the original sound
select Sound 'name$'
Remove
endfor
endif
# PRINT THE PROCESSING PARAMETERS USED
if verbose !=1
#clearinfo
endif
print 'newline$'
call printCornerFreqs
call print_vocoder_settings
if run_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
# DONE
print 'newline$'DONE!!!
#############################################################################
#############################################################################
# PROCEDURES
#############################################################################
#############################################################################
# The main procedure,
# which contains a bunch of subroutines
procedure vocode name$
# Get Sound info
select Sound 'name$'
start = Get start time
end = Get end time
samplerate = Get sampling frequency
if verbose == 1
print vocoding 'name$''newline$'
endif
# Coerce number of signal channels variable to be 1
numberOfSignalChannels = 1
#===============================================================#
# MAKE CHANNEL ENVELOPES
call create_envelopes 'name$' numberOfChannels rolloff.per.mm envelope
#===============================================================#
# PEAK-PICKING
if numberStimulated != numberOfChannels
call peakpick_envelopes numberOfChannels numberStimulated window_refresh_rate
endif
#===============================================================#
# MAKE THE CARRIERS
call make_carriers
#===============================================================#
# COMBINE THE CHANNELS
select Sound carrier_channel_1
Copy... temp_vocoded
if numberOfChannels > 1
for chan_index from 2 to numberOfChannels
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
#===============================================================#
# Remove channels
if remove_original_channels == 1
for chan_index from 1 to numberOfChannels
select Sound 'name$'_channel_'chan_index'
Remove
endfor
endif
endproc
procedure create_envelopes .sound$ .numAnalysisChannels .rolloff.per.mm .envelopeCutoffFreq
# This is the workhorse function series to create time-series (sounds)
# that represent the envelopes of each channel.
# yields: channel_'.thisChannel'_ENV_step_3_LPF
#
# First, get some basic info
select Sound '.sound$'
.duration = Get total duration
.samplerate = Get sampling frequency
# first preemphasize
Filter (pre-emphasis): 500
Rename... '.sound$'
#===============================================================#
# DIVIDE SOUND INTO ANALYSIS BANDS
if verbose == 1
print dividing into analysis bands... 'newline$'
endif
select Sound '.sound$'
call divideIntoAnalysisChannels '.sound$'
#yields: '.sound$'_channel_'.thisChannel'
# for all channels
# Remove the temporarily preemphasized sound
# because it shares a common name with the original sound object
select Sound '.sound$'
Remove
#===============================================================#
# MAKE THE ENVELOPES
for .thisChannel from 1 to .numAnalysisChannels
if verbose == 1
print CHANNEL '.thisChannel''newline$'
endif
# First get the original intensity
select Sound '.sound$'_channel_'.thisChannel'
channel_'.thisChannel'_intensity = Get intensity (dB)
#---------------------------------------------------#
# Ensure that the intensity is above 0
if channel_'.thisChannel'_intensity < 0
channel_'.thisChannel'_intensity = 0.01
endif
#===============================================================#
#===============================================================#
# Step 1: EXTRACT THE ENVELOPE
if use_Hilbert == 1
# Get HILBERT transform envelope
if verbose == 1
print Extracting envelope '.thisChannel' using Hilbert transform... 'newline$'
endif
call do_Hilbert_transform '.sound$'_channel_'.thisChannel'
#-------------------------------------#
# envelope is "'.sound$'_channel_'.thisChannel'_ENV"
# TFS is "'.sound$'_channel_'.thisChannel'_TFS"
#-------------------------------------#
# remove TFS (we don't need that)
select Sound '.sound$'_channel_'.thisChannel'_TFS
Remove
else
# FAST ENVELOPE using Intensity Tier, etc
if verbose == 1
print Extracting envelope '.thisChannel' using Intensity Tier... 'newline$'
endif
call fast_envelope '.sound$'_channel_'.thisChannel'
# envelope is "'.sound$'_channel_'.thisChannel'_ENV"
endif
if preserve_envelopes == 1
select Sound '.sound$'_channel_'.thisChannel'_ENV
Copy... env_ch_'.thisChannel'_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
if verbose == 1
print Compressing Envelope '.thisChannel''newline$'
endif
call compress_envelope '.sound$'_channel_'.thisChannel'_ENV compression_mult .envelopeCutoffFreq
# yields '.sound$'_channel_'.thisChannel'_ENV_compressed
if verbose == 1
print envelope has been compressed 'newline$'
endif
select Sound '.sound$'_channel_'.thisChannel'_ENV_compressed
Rename... channel_'.thisChannel'_ENV_step_2
else
# Just rename it to match the expected naming scheme
select Sound '.sound$'_channel_'.thisChannel'_ENV
Rename... channel_'.thisChannel'_ENV_step_2
endif
if preserve_envelopes == 1
select Sound channel_'.thisChannel'_ENV_step_2
Copy... env_ch_'.thisChannel'_2_compressed
endif
#===============================================================#
#===============================================================#
# Step 3: LOW-PASS FILTER THE ENVELOPE
if verbose == 1
print Low-pass filtering envelope '.thisChannel' 'newline$'
endif
# if you've got a number above zero in the LPF argument,
# use it as the upper limit of a low-pass filter
if .envelopeCutoffFreq > 0
select Sound channel_'.thisChannel'_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, .envelopeCutoffFreq, 2
Rename... channel_'.thisChannel'_ENV_step_3_LPF
# remove near-silence
if remove_near_silence
Formula: "if self[col] < silence_threshold then 0 else self endif"
endif
if verbose == 1
print Envelope '.thisChannel' has been low-pass filtered 'newline$'
endif
if preserve_envelopes == 1
select Sound channel_'.thisChannel'_ENV_step_3_LPF
Copy... env_ch_'.thisChannel'_3_compressed_LPF
endif
select Sound full_envelope
Remove
#===============================================================#
#===============================================================#
# SCRAMBLE envelope phase
elsif .envelopeCutoffFreq == 0
# Secret code to SCRAMBLE envelope phase
if verbose == 1
print Scrambling Envelope '.thisChannel' phase 'newline$'
endif
select Sound channel_'.thisChannel'_ENV_step_2
env_LPF = 50
keep_periodicity = 1
draw_env = 0
call modify_ENV channel_'.thisChannel'_ENV_step_2 env_LPF scramble keep_periodicity scramble_mix _scrambled draw_env
select Sound channel_'.thisChannel'_ENV_scrambled
Rename... envelope_LPF
#===============================================================#
# Secret code to INVERT the envelope
elsif .envelopeCutoffFreq == -1
if verbose == 1
print Inverting Envelope '.thisChannel' 'newline$'
endif
select Sound channel_'.thisChannel'_ENV_step_2
env_LPF = 50
keep_periodicity = 1
draw_env = 0
call modify_ENV channel_'.thisChannel'_ENV_step_2 env_LPF scramble keep_periodicity scramble_mix _inverted draw_env
select Sound channel_'.thisChannel'_ENV_inverted
Rename... envelope_LPF
endif
endfor
endproc
procedure fast_envelope .name$
# This was written because doing a Hilbert transform
# can be very computationally expensive/slow
#-------------------------------------#
# 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 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"
if verbose == 1
print peak-picking...'newline$'
endif
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
#select Sound channel_'chan_index'_ENV_step_3_LPF_band_'resamplerate'
#minimum = Get minimum: 0, 0, "None"
#if minimum < 0
#Formula: "self[col] - 'minimum'"
#endif
# just 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 (which contained periodicity)
# with the peak-picked envelope
# so that the periodicity is maintaned,
# but the entire envelope energy is affected
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
# 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
if verbose == 1
print done peak-picking...'newline$'
endif
# 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 numberOfChannels
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
# create the wideband carrier that will be filtered into channels
if verbose == 1
print creating carriers... 'newline$'
endif
#=======================================================#
#=======================================================#
# DIFFERENT SETUP DEPENDING ON CARRIER CHANNEL TYPE
if carrier_type$ = "noise"
if shape$ != "LNN_premade"
# create one white noise now,
# to be narrowband filtered later
do ("Create Sound from formula...", "carrier", numberOfSignalChannels, 0, end, samplerate, "randomGauss(0,0.1)")
endif
elsif carrier_type$ = "toneComplex"
# 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
Create Sound as tone complex: "carrier", 0, end, samplerate, "Sine", pulseRate, 0, 0, 0
elsif carrier_type$ = "sinewave"
# ... need to wait until channel loop
elsif carrier_type$ = "PSHC"
# open a pre-made pulse-spreading harmonic complex
call open_PSHC .duration .samplerate
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 .thisChannel from 1 to numberOfChannels
if verbose == 1
print creating carrier for channel '.thisChannel''newline$'
endif
#===============================================================#
# SINEWAVE CARRIER
if carrier_type$ == "sinewave"
cf = carrier_freq_center_'.thisChannel'
call make_sinewave cf end samplerate carrier
endif
#===============================================================#
# ORIGINAL SOUND AS CARRIER
if carrier_type$ == "original_sound"
select Sound 'name$'
Copy... carrier
endif
#===============================================================#
# LOW-NOISE NOISE CARRIER
# and do the envelope multiplication inline
if shape$ = "LNN"
cf = carrier_freq_center_'.thisChannel'
call make_LNN_ERB cf end samplerate carrier
endif
# Special case for when you've already generated your LNN channels
if shape$ = "LNN_premade"
# call up pre-made LNN sounds
call open_LNN_channel 'prearranged_LNN_channel_folder$' 'file_prefix_LNN$' '.thisChannel'
temp_LNN$ = selected$("Sound")
duration_LNN = Get total duration
# get some frequency infor that we'll use later for filtering
cf = premade_LNN_cfs#['.thisChannel']
call calculate_LNN_ERB_edges cf
file_index_mod = file_index mod 10
# that number ranges from 0 to 9
# we want a value that ranges from 1 to 10
file_index_mod_alt = file_index_mod + 1
# define 11 values between 0 and 1
# (we'll use the first 10, as the 11th is the same as the first)
call makeContinuum 11 0 duration_LNN lnn_time_shift_ 0
# index that continuum
time_to_shift = lnn_time_shift_'file_index_mod_alt'
# circle-shift the LNN carrier
lnn_shift_blend_time = 0.2
call circle_shift_blend 'temp_LNN$' time_to_shift lnn_shift_blend_time temp_LNN_circle_shifted
# extract only the duration needed to process this sound
select Sound temp_LNN_circle_shifted
Extract part: 0, end, "rectangular", 1, "no"
Rename... carrier
# cleanup
select Sound temp_LNN_circle_shifted
plus Sound 'temp_LNN$'
Remove
endif
#===============================================================#
# NB NOISE OR SINEWAVE CARRIER: THE ENVELOPE PART
# if it's not LNN or the original, multiply the carrier by the envelope now
# multiply the noise by the envelope
# modified from the original sound channel
if shape$ != "original"
select Sound carrier
Copy... broad_carrier_channel_'.thisChannel'_w_ENV
Formula... self[col] * Sound_channel_'.thisChannel'_ENV_step_3_LPF[col]
endif
#===============================================================#
# NB NOISE OR SINEWAVE CARRIER: THE FILTERING PART
# Filter the output channel to match the desired bandwidth
call filter_carrier_channel .thisChannel
# yields "carrier_channel_'.thisChannel'"
#===============================================================#
#===============================================================#
# 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_'.thisChannel'
# quantized envelope LPF
int_quant_lpf = 100
call quantize_intensity carrier_channel_'.thisChannel' num_intensity_steps min_input_range max_input_range int_quant_lpf
select Sound carrier_channel_'.thisChannel'
# rename & update object name status
Rename... temp_to_delete
select Sound carrier_channel_'.thisChannel'_int_quant
Rename... carrier_channel_'.thisChannel'
select Sound temp_to_delete
Remove
endif
#===============================================================#
# end loop through channels
endfor
endproc
procedure open_LNN_channel .prearranged_LNN_channel_folder$ .file_prefix_LNN$ .thisChannel
Read from file: "'.prearranged_LNN_channel_folder$'/'.file_prefix_LNN$''.thisChannel'.wav"
endproc