5 sentence Recorded_audio ookhetweer.wav
6 comment Entering -999 in any field disables the feature or change
7 real Pitch 70 (= Hz, mean)
8 real Pitch_SD 15 (= % of mean)
9 real Duration 1.3 (= mult. factor)
11 real Bubbles 1 (= fraction, inactive)
12 real Bubbles_SNR 10 (= dB SNR)
15 positive Voicing_floor_(dB) 15 (= below maximum)
19 ########################################################################
21 # VoiceConversion.praat
23 # Change the input speech to resemble Tracheoesophageal speech.
24 # Changes the Pitch (F0) and pitch movements, duration. Filtered noise
25 # is added as well as filtered "bubble" sounds.
26 # Increase the Jitter and Shimmer of a speech recording to the
27 # number given. Cannot reduce Jitter or Shimmer.
28 # Note that Jitter and Shimmer are ill-defined in anything but
31 # Uses the To PointProcess (periodic, cc) to calculate the jitter
32 # and To PointProcess (periodic, peaks): 60, 300, "yes", "yes"
33 # to change the timing of the periods.
35 # Periods are moved with Overlap-and-Add
37 # Shimmer is adapted using additive noise over an intensity tier and
38 # adapting each period individually. Periods are determined with the
39 # To PointProcess (periodic, peaks) pulses.
41 ########################################################################
43 # Copyright (C) 2016-2017 NKI-AVL, R. J. J. H. van Son
46 # This program is free software: you can redistribute it and/or modify
47 # it under the terms of the GNU General Public License as published by
48 # the Free Software Foundation, either version 3 of the License, or
49 # (at your option) any later version.
51 # This program is distributed in the hope that it will be useful,
52 # but WITHOUT ANY WARRANTY; without even the implied warranty of
53 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54 # GNU General Public License for more details.
56 # You should have received a copy of the GNU General Public License
57 # along with this program. If not, see <http://www.gnu.org/licenses/>.
59 # Full license text is available at:
60 # http://www.gnu.org/licenses/gpl-3.0.html
62 ########################################################################
64 # Input parameters (<=-999 means "do not change"):
66 # Input file A file name (with full path). If a Sound object is selected, that will be used instead
67 # Pitch Average pitch of the new speech in Hz [F'(t) = Fnew/Fold * F(t)]
68 # Pitch SD Standard deviation of the Pitch of the new speech in Hz (compresses pitch movements)
69 # [SD'(t) = SDnew/SDold * (F(t) - Faverage) + Faverage]
70 # Duration Factor with which to multiply the duration
71 # HNR Signal to Noise ratio of new noise added to obtain the HNR given
72 # Bubbles Fraction of time bubble sounds are the source (0-1, 0 = disable). Use source "TE_source_bubbles.wav"
73 # Bubbles SNR Signal to Noise ratio of bubble source (in dB)
74 # Jitter New jitter in %
75 # Shimmer New Shimmer in %
76 # Voicing floor Lowest level of sound still considered voiced, in dB below the maximum
78 # Help Print this text and exit
81 # The input sound converted according to the specifications
83 # Print debugging information
88 # A Praat Sound object with the transformed speech
91 # praat VoiceConversion.praat Speech/Example1.wav 70 15 1.3 5 5 15 5 10 15 no
99 printline Input parameters (<=0 means "do not change"):
100 printline Input file'tab$''tab$'A file name (with full path). If a Sound object is selected, that will be used instead
101 printline Pitch'tab$''tab$''tab$'Average pitch of the new speech in Hz [F'(t) = Fnew/Fold * F(t)]
102 printline Pitch SD'tab$''tab$'Standard deviation of the Pitch of the new speech in Hz (compresses pitch movements)
103 printline 'tab$''tab$''tab$''tab$'[SD'(t) = SDnew/SDold * (F(t) - Faverage) + Faverage]
104 printline Duration'tab$''tab$'Factor with which to multiply the duration
105 printline HNR'tab$''tab$''tab$''tab$'Signal to Noise ratio of new noise added to obtain the HNR given
106 printline Bubbles'tab$''tab$''tab$'Fraction of time bubble sounds are the source (0-1, 0 = disable). Use source "TE_source_bubbles.wav"
107 printline Bubbles SNR'tab$''tab$'Signal to Noise ratio of bubble sounds added (in dB)
108 printline Jitter'tab$''tab$''tab$'New jitter in %
109 printline Shimmer'tab$''tab$''tab$'New Shimmer in %
110 printline Voicing floor'tab$'Lowest level of sound still considered voiced, in dB below the maximum
112 printline Help'tab$''tab$''tab$'Print this text and exit
115 printline The input sound converted according to the specifications
122 if numberOfSelected("Sound") > 0
123 .recordedSound = selected("Sound")
124 elsif recorded_audio$ <> "" and fileReadable(recorded_audio$)
125 .recordedSound = Read from file: recorded_audio$
126 Rename: "RecordedSpeech"
129 bubblesAudioName$ = "bubbles.wav"
131 te_source_bubbles_name$ = "TE_source_bubbles.wav"
133 .thresshold = -voicing_floor
136 select .recordedSound
137 global.setIntensity = Get intensity (dB)
139 call convert_speechOverlapAndAdd .recordedSound .thresshold jitter shimmer pitch pitch_SD duration hNR bubbles bubbles_SNR
141 # Definitions of functions
144 procedure convert_speechOverlapAndAdd .recordedSound .thresshold .jitter .shimmer .pitch .pitch_SD .durationFactor .newHNR .bubble_fraction .bubble_snr
145 call change_pitch_duration .recordedSound .pitch .pitch_SD .durationFactor
146 .newPitchSound = selected("Sound")
147 .duration = Get total duration
148 .sampleFreq = Get sampling frequency
150 call extractVoicingParameters .newPitchSound .thresshold
151 .recordedTextGrid = selected("TextGrid")
152 .recordedPulses = selected("PointProcess")
153 .recordedInt = selected("Intensity")
155 select .newPitchSound
156 .recordedPulsesPeaks = To PointProcess (periodic, peaks): 60, 300, "yes", "yes"
159 call create_additive_noise .newPitchSound .recordedTextGrid
160 .additiveNoise = selected("Sound")
162 .additiveNoise = Create Sound from formula: "WhiteNoise", 1, 0, .duration, .sampleFreq, "0"
165 #call add_bubbles '.newPitchSound' '.bubble_fraction' '.bubble_snr' '.recordedTextGrid' 'bubblesAudioName$'
166 #.additiveBubbles = selected("Sound")
168 # Change Jitter, use CC to determine jitter and Peaks to change the periods
169 selectObject: .recordedPulsesPeaks
170 .newPointProcess = Copy: "New_Pulses"
171 call set_jitter .jitter .newPointProcess .recordedPulses
172 call apply_overlap_add .newPitchSound .recordedPulsesPeaks .recordedTextGrid .newPointProcess .shimmer
173 .newSound = selected("Sound")
175 # Create bubbles from source
176 if .bubble_fraction > 0 and .bubble_snr > -999
177 call synth_with_source_signal '.newPitchSound' '.recordedPulses' .bubble_fraction .bubble_snr 'te_source_bubbles_name$'
178 .additiveBubbles = selected("Sound")
180 .additiveBubbles = Create Sound from formula: "WhiteNoise", 1, 0, .duration, .sampleFreq, "0"
186 selectObject: .recordedPulses
187 .old_jitter = Get jitter (local): 0, 0, 0.0001, 0.03, 2
188 selectObject: .newPointProcess
189 .newPPjitter = Get jitter (local): 0, 0, 0.0001, 0.03, 2
190 selectObject: .recordedSound
192 .old_amplitude = To AmplitudeTier (period): 0, 0, 0.0001, 0.03, 2
193 .old_shimmer = Get shimmer (local): 0.0001, 0.03, 2
196 selectObject: .newSound
197 .pointP = To PointProcess (periodic, cc): 60, 300
198 .new_jitter = Get jitter (local): 0, 0, 0.0001, 0.03, 2
200 selectObject: .newSound
202 .new_amplitude = To AmplitudeTier (period): 0, 0, 0.0001, 0.03, 2
203 .new_shimmer = Get shimmer (local): 0.0001, 0.03, 2
205 appendInfoLine: "New Jitter: '.new_jitter:1%' ('.old_jitter:1%' ~> '.newPPjitter:1%')"
206 appendInfoLine: "New Shimmer: '.new_shimmer:1%' ('.old_shimmer:1%')"
208 selectObject: .old_amplitude, .pointP, .new_amplitude
212 # Add noise to result
213 call add_sounds .newSound .additiveNoise .newHNR
214 .resultNoise = selected("Sound")
217 # Add bubbles to result
218 call add_sounds .resultNoise .additiveBubbles .bubble_snr
219 .result = selected("Sound")
223 selectObject: .newPitchSound, .recordedTextGrid, .recordedPulses, .recordedInt, .newPointProcess, .recordedPulsesPeaks, .newSound, .additiveNoise, .additiveBubbles, .resultNoise
226 selectObject: .result
230 procedure change_pitch_duration .sound .pitch .pitchFraction .durationFactor
232 .duration = Get total duration
234 .manipulation = To Manipulation: 0.01, 70, 300
235 .pitchTier = Extract pitch tier
236 .currentPitch = Get mean (points): 0, 0
237 .pitch_SD = .pitchFraction / 100 * .pitch
240 .durationTier = Extract duration tier
243 if .durationFactor > 0
244 .numPoints = Get number of points
248 Formula: "self*'.durationFactor'"
251 Replace duration tier
256 .factor = (.pitch / .currentPitch)
257 Multiply frequencies: 0, .duration, .factor
258 .currentSD = Get standard deviation (points): 0, 0
261 .factor = .pitch_SD / .currentSD
262 Formula: "'.pitch' + (self - '.pitch') * '.factor'"
271 if .currentPitch > 0 or .durationFactor > 0
273 .newSound = Get resynthesis (overlap-add)
276 .newSound = Copy: "New Sound"
286 procedure extractVoicingParameters .recordedSound .thresshold
287 # The lowest level of voiced sounds
288 select .recordedSound
289 .pointPcc = To PointProcess (periodic, cc): 60, 300
290 Rename: "RecordedPulses"
291 .textGrid = To TextGrid (vuv): 0.02, 0.01
292 Rename: "RecordedVoicing"
293 .numIntervals = Get number of intervals: 1
295 # Correct voicing boundaries
296 select .recordedSound
297 .intensity = To Intensity: 100, 0, "yes"
298 Rename: "RecordedIntensity"
299 .silences = To TextGrid (silences): .thresshold, 0.1, 0.05, "silent", "sounding"
302 for .i to .numIntervals
304 .label$ = Get label of interval: 1, .i
306 .start = Get starting point: 1, .i
307 .end = Get end point: 1, .i
309 # Starting point of voiced interval
311 .s = Get interval at time: 1, .start
312 .sLabel$ = Get label of interval: 1, .s
313 if .sLabel$ = "silent"
314 .sStart = Get starting point: 1, .s
315 .sEnd = Get end point: 1, .s
318 Set interval text: 1, .i, "U"
319 # Shift boundaries: Insert&Remove
320 Insert boundary: 1, .sEnd
321 Set interval text: 1, .i+1, "V"
323 Set interval text: 1, .i, ""
324 Remove left boundary: 1, .i
327 # Low intensity, unvoiced
328 Set interval text: 1, .i, "U"
335 for .i to .numIntervals
337 .label$ = Get label of interval: 1, .i
339 .start = Get starting point: 1, .i
340 .end = Get end point: 1, .i
342 # Starting point of voiced interval
344 .s = Get interval at time: 1, .end
345 .sLabel$ = Get label of interval: 1, .s
346 if .sLabel$ = "silent"
347 .sStart = Get starting point: 1, .s
348 .sEnd = Get end point: 1, .s
351 Set interval text: 1, .i, "U"
352 # Shift boundaries: Insert&Remove
353 Insert boundary: 1, .sStart
354 Set interval text: 1, .i, "V"
356 Set interval text: 1, .i+1, ""
357 Remove right boundary: 1, .i+1
360 # Low intensity, unvoiced
361 Set interval text: 1, .i, "U"
375 # LPC analysis resynthesis with white noise as the new source. Only
376 # resynthesize the voiced parts.
377 procedure create_additive_noise .sound .vuvTextGrid
379 .duration = Get total duration
380 .sampleFreq = Get sampling frequency
382 .additiveNoiseSound = -1
386 .downsampled = Resample: 10000, 50
387 .lpc = To LPC (autocorrelation): 10, 0.025, 0.005, 50
389 .source = Filter (inverse)
390 .sourceInt = To Intensity: 70, 0, "yes"
391 .sourceIntTier = To IntensityTier (peaks)
393 # Create additive noise
394 .noise = Create Sound from formula: "WhiteNoise", 1, 0, .duration, .sampleFreq, "randomGauss(0,0.1)"
396 .filteredNoise = Filter: "no"
398 .additiveNoiseSoundTMP = Multiply: "yes"
399 call set_VUV_to_zero .additiveNoiseSoundTMP .vuvTextGrid U
400 .additiveNoiseSound = Resample: .sampleFreq, 50
402 selectObject: .noise, .filteredNoise, .additiveNoiseSoundTMP
405 selectObject: .downsampled, .lpc, .source, .sourceInt, .sourceIntTier
408 if .additiveNoiseSound <= 0
409 .additiveNoiseSound = Create Sound from formula: "AdditiveNoise", 1, 0, .duration, .sampleFreq, "0"
412 select .additiveNoiseSound
415 # Add sounds. If either sounds does not exist, use the other.
416 # If the s1/s2 ration <= -999, copy the first sound (if it exists)
417 # else, copy the second source.
418 procedure add_sounds .sound1 .sound2 .s1_s2ratioDB
424 if .sound1 <= 0 and .sound2 <= 0
425 exitScript: "add_sounds: No sounds to add"
428 if .s1_s2ratioDB <> -999
431 .tmp1 = Copy: "Sound1"
432 .int1 = Get intensity (dB)
436 .duration = Get total duration
437 .sampleFreq = Get sampling frequency
442 .tmp2 = Copy: "Sound2"
443 .int2 = Get intensity (dB)
447 .duration = Get total duration
448 .sampleFreq = Get sampling frequency
452 .tmp1 = Create Sound from formula: "BubblesNoise1", 1, 0, .duration, .sampleFreq, "0"
454 .tmp2 = Create Sound from formula: "BubblesNoise2", 1, 0, .duration, .sampleFreq, "0"
457 if .int1 - .int2 <> .s1_s2ratioDB
458 .ratio = .s1_s2ratioDB - (.int1 - .int2)
460 Scale intensity: .int1 + .ratio / 2
462 Scale intensity: .int2 - .ratio / 2
464 selectObject: .tmp1, .tmp2
465 .stereo = Combine to stereo
466 .addedSound = Convert to mono
468 selectObject: .stereo, .tmp1, .tmp2
472 selectObject: .sound1
474 selectObject: .sound2
477 .addedSound = Copy: "BubblesNoise"
480 selectObject: .addedSound
484 # Set Jitter to a specified number
486 # Ti = ti - ti-1 (interval i)
487 # Jitter (absolute) is Sum[ abs(Ti - Ti-1) ] / N-1
488 # Jitter = Jitter (absolute) / mean(Ti)
490 # For a Normal distribution
491 # E(|X|) = sqrt(2/pi) * stdev(X)
494 # E(Ti^2) = var(Ti) + E(Ti)^2
495 # E(Ti*Ti-1) = cor(Ti, Ti-1) + E(Ti)^2
496 # var(Ti - Ti-1) = E(Ti^2 - 2*Ti*Ti-1 + Ti-1^2)
497 # = 2*E(Ti^2) - 2*E(Ti*Ti-1)
498 # = 2*[ var(Ti) * (1 - cor(Ti, Ti-1)) ]
500 # Combine, assuming a Normal distribution:
501 # Jitter = E(|Ti - Ti-1|) / E(Ti)
502 # = sqrt(2/pi) * stdev(Ti - Ti-1) / mean(Ti)
503 # = sqrt(2/pi * var(Ti - Ti-1)) / mean(Ti)
504 # = sqrt[ 4/pi * ( var(Ti) * (1 - cor(Ti, Ti-1)) ) ] / mean(Ti)
506 # Change Ti -> T'i; Jitter -> a*Jitter while keeping mean(Ti) = mean(T'i) constant
507 # ei = (ti + ti-2)/2 - ti-1
508 # Jitter' = a * Jitter
509 # = a * sqrt[ 4/pi * var(Ti - Ti-1) ] / mean(Ti)
511 # => var(T'i - T'i-1) = a^2 * var(Ti - Ti-1)
512 # = a^2 * E[ (Ti - Ti-1)^2 ]
513 # = a^2 * E[ (ti - ti-1 - ti-1 + ti-2)^2 ]
514 # = a^2 * 2 * E[ ((ti + ti-2)/2 - ti-1)^2 ]
515 # = a^2 * 2 * E[ ei^2 ]
516 # = 2 * E[ (a*ei)^2 ]
519 # Generalizing, var(T'i - T'i-1) = 2*(var(ti-1) + var(ti) + var(ti+1))
520 # To increase Jitter -> Jitter'
521 # 1) Determine var(Ti - Ti-1) = (Jitter * mean(Ti))^2 * pi / 2
522 # 2) Calculate var(T'i - T'i-1) = (Jitter' * mean(T'i))^2 * pi / 2
523 # 3) Determine var to add:
524 # add_var(Ti - Ti-1) = var(T'i - T'i-1) - var(Ti - Ti-1)
525 # 4) Var of Noise to add per ti: add_var(ti) = add_var(Ti - Ti-1)/(2*3)
526 # 5) Sd of Noise to add per ti: add_sd(ti) = sqrt(add_var(ti))
529 # Converts .pulses into pulses with new Jitter
530 procedure set_jitter .newJitter .pulses .pulsesCC
532 if .pulses > 0 and .newJitter > 0
535 # Use CC to determine real jitter
539 .current_jitter = Get jitter (local): 0, 0, 0.0001, 0.03, 2
540 .current_abs_jitter = Get jitter (local, absolute): 0, 0, 0.0001, 0.03, 2
541 .current_mean_period = Get mean period: 0, 0, 0.0001, 0.03, 2
542 .current_stdev_period = Get stdev period: 0, 0, 0.0001, 0.03, 2
544 if .newJitter > .current_jitter
545 .current_var = .current_abs_jitter**2 * pi/2
546 .end_var = (.newJitter * .current_mean_period)**2 * pi/2
547 # The variance to add per boundary (total / (2*3))
548 .add_var = (.end_var - .current_var) / 6
549 .stdev_e = sqrt(.add_var)
551 # Keep the original pulses just is case the order of the pulses might change
553 .origPulses = Copy: "Original_Pulses"
554 .numPoints = Get number of points
557 # Change jitter by moving the ti according to
558 # t'i = ti - randomGauss(0, stdev(e'))
559 for .p from 1 to .numPoints
561 .t = Get time from index: .p
562 .new_t = .t - randomGauss(0, .stdev_e)
564 # Remove current point
566 .r = Get nearest index: .t
574 pause New jitter: '.newJitter' must be larger than current jitter '.current_jitter:4'
577 # Calculate new jitter
579 .jitter_new = Get jitter (local): 0, 0, 0.0001, 0.03, 2
581 .current_jitter *= 100
588 # We cannot use the shimmer of a sentence, so we can only "add" shimmer
590 # .new_shimmer is in %
591 # .sound: Source Sound
592 # .pulses: PointProcess
593 # .voicing: VUV TextGrid
594 # .new_shimmer: New shimmer in %
595 procedure increase_shimmer .sound .pulses .voicing .newShimmer
600 # Create Amplitude Tier and get current shimmer
602 .duration = Get total duration
604 .current_amplitude = To AmplitudeTier (period): 0, 0, 0.0001, 0.03, 2
605 .current_shimmer = Get shimmer (local): 0.0001, 0.03, 2
606 select .current_amplitude
607 .numPoints = Get number of points
608 .ampreal = Down to TableOfReal
611 for .p from 1 to .numPoints
613 .tmp = Get value: .p, 2
619 .meanAmp = .sumamp / .n
621 # Sd must be multiplied with the amplitude
622 if .newShimmer > .current_shimmer
623 .new_var = (.newShimmer**2 - .current_shimmer**2) * .meanAmp**2 * pi / 2
625 .new_var = .newShimmer**2 * .meanAmp**2 * pi / 2
628 .new_sd = sqrt(.new_var / 2)
633 .new_amplitude = Create AmplitudeTier: "New_Amplitude", 0, .duration
634 for .p from 1 to .numPoints
636 .t = Get value: .p, 1
637 .a = Get value: .p, 2
642 .new_a = .a - randomGauss(0, .new_sd)
648 select .new_amplitude
649 Add point: .t, .new_a / .a
655 # Set unvoiced parts to 1
656 select .new_amplitude
660 .numIntervals = Get number of intervals: 1
661 for .i from 1 to .numIntervals
663 .t = Get end point: 1, .i
664 select .new_amplitude
669 plus .current_amplitude
672 # Overlay shimmer over sound
675 .new_sound = Multiply
676 Rename: "NewSound_Shimmer"
680 .shimmer_new = Get shimmer (local): 0, 0, 0.0001, 0.02, 1.3, 1.6
682 .current_shimmer *= 100
685 select .new_amplitude
690 .new_sound = Copy: "NewSound_Shimmer"
696 # Make a copy of the source to the target matching the pulses in source and target
697 # Copies fragments around pulses in sourcePulses under the direction of the
698 # corresponding pulses in targetPulses using the Overlap&Add method (Gaussian window)
700 # Ignores voiceless parts, ie, intervals between pulses > .maxInt
701 # For voices, .maxInt should be ~0.02 (F0 > 50Hz). For other sounds, e.g., bubbles, this
702 # should be increased to fit the whole sound between pulses.
704 # Midpoint between the pulses, periods add up to a factor of ~1.04.
705 # At the pulses themselves, it adds up to ~1.12 (summed left and right)
707 procedure overlap_add .sourceSound .sourcePulses .targetSound .targetPulses .maxInt
708 # Create empty .targetSound if .targetSound does not exist
711 .duration = Get total duration
712 .samplingFrequency = Get sampling frequency
713 .targetSound = Create Sound from formula: "Target Sound", 1, 0, .duration, .samplingFrequency, "0"
715 # Default, just copy the source pulses
716 if .targetPulses <= 0
717 .targetPulses = .sourcePulses
720 # Maximum interval between pulses (maximum pitch period)
726 .sourceName$ = replace_regex$(selected$(), " ", "_", 0)
728 .targetName$ = replace_regex$(selected$(), " ", "_", 0)
730 # Iterate over target pulses
732 .numPulses = Get number of points
736 .tTarget = Get time from index: .p
737 .pLeft = Get interval: .tTarget - 0.001
738 .pRight = Get interval: .tTarget + 0.001
741 .q = Get nearest index: .tTarget
742 .tSource = Get time from index: .q
743 .qLeft = Get interval: .tSource - 0.001
744 .qRight = Get interval: .tSource + 0.001
745 # Gaussian window parameters (FWHM Left and Right)
746 # FWHM = 2*sqrt(2*ln(2)) * c
747 .cL = min(.pLeft,.qLeft)/(2*sqrt(2*ln(2)))
748 .cR = min(.pRight,.qRight)/(2*sqrt(2*ln(2)))
749 if not( .cL = undefined or .cL > .maxInt/(2*sqrt(2*ln(2))) or .cR = undefined or .cR > .maxInt/(2*sqrt(2*ln(2))) )
752 Formula (part): .tTarget-.margin, .tTarget+.margin, 1, 1, "if x<.tTarget then self + '.sourceName$'((x - .tTarget) + .tSource)*exp(-1*(((x - .tTarget)/.cL)^2)/2) else self + '.sourceName$'((x - .tTarget) + .tSource)*exp(-1*(((x - .tTarget)/.cR)^2)/2) endif"
760 procedure apply_overlap_add .sourceAudio .sourcePulses .vuvTextGrid .targetPulses .newShimmer
761 # Use overlap-add to add new source intervals
762 # Copy only voiced pulses
763 call set_VUV_to_zero .targetPulses .vuvTextGrid U
764 # Create a copy of the old source with voiced parts zeroed
766 .testSource = Copy: "OaAsound"
767 call set_VUV_to_zero .testSource .vuvTextGrid V
769 # Copy the voiced parts of the new source to the zeroed voiced parts of the old source
770 call overlap_add .sourceAudio .sourcePulses .testSource .targetPulses 0.02
771 call increase_shimmer .testSource .targetPulses .vuvTextGrid .newShimmer
772 .newSound = selected("Sound")
773 Scale intensity: global.setIntensity
782 # Set intervals matching a label text to Zero or remove the pulses
783 # Works on Sound and Pulses
784 procedure set_VUV_to_zero .sound .vuvTextGrid .zeroLabel$
786 .objectType$ = selected$()
787 .objectType$ = extractWord$ (.objectType$, "")
789 .numIntervals = Get number of intervals: 1
790 # Zero out VU intervals
791 for .i to .numIntervals
793 .vuvLabel$ = Get label of interval: 1, .i
794 .start = Get starting point: 1, .i
795 .end = Get end point: 1, .i
796 if .vuvLabel$ = .zeroLabel$
798 if .objectType$ = "Sound"
799 Set part to zero: .start, .end, "at nearest zero crossing"
800 elsif .objectType$ = "PointProcess"
801 Remove points between: .start, .end
803 printline Unsupported object type for set_VUV_to_zero
812 # LPC analysis-resynthesis with source. Ignore voice/voiceless
815 procedure synth_with_source_signal .sound .pulses .fraction .snr .sourceAudioName$
818 .duration = Get total duration
819 .samplingFrequency = Get sampling frequency
826 .targetIntensity = Get intensity (dB)
827 .targetDuration = Get total duration
828 .downsampled = Resample: 10000, 50
829 .lpc = To LPC (autocorrelation): 10, 0.025, 0.005, 50
831 .source = Filter (inverse)
832 .sourceInt = To Intensity: 70, 0, "yes"
833 .sourceIntTier = To IntensityTier (peaks)
839 # Create the taget file
840 .masterSourceSound = Read from file: .sourceAudioName$
841 .masterDuration = Get total duration
842 while .masterDuration < 2*.duration
843 .tmpA = .masterSourceSound
846 .masterSourceSound = Concatenate
847 selectObject: .tmpA, .tmpB
849 select .masterSourceSound
850 .masterDuration = Get total duration
852 # Get a random start point
853 .startPoint = randomUniform (0, .duration)
854 select .masterSourceSound
855 .targetSound = Extract part: .startPoint, .startPoint+.duration, "rectangular", 1, "no"
857 select .masterSourceSound
862 .scaledBubbleSource = Multiply: "yes"
863 Scale intensity: .targetIntensity - .snr
865 selectObject: .targetSound, .sourceIntTier
868 select .scaledBubbleSource
870 .filteredBubbleSource = Filter: "no"
871 Rename: "TargetBubbleSound"
872 .targetSound = Resample: .samplingFrequency, 50
874 selectObject: .scaledBubbleSource, .lpc, .filteredBubbleSource
878 Scale intensity: .targetIntensity - .snr
880 .targetSound = Create Sound: "Bubbles", 0, .duration, .samplingFrequency, "0"
888 # Select a random puls in the bubbles and add it to a random puls in the target
890 # Creates a sound with only the bubbles
892 procedure add_bubbles .sound .rate .snr .vuvTextGrid .bubblesAudioName$
895 .targetIntensity = Get intensity (dB)
896 .targetDuration = Get total duration
897 .tagetSamplingFrequency = Get sampling frequency
898 .targetNumBubbles = .rate * .targetDuration
899 .downsampled = Resample: 10000, 50
900 .lpc = To LPC (autocorrelation): 10, 0.025, 0.005, 50
902 .source = Filter (inverse)
903 .sourceInt = To Intensity: 70, 0, "yes"
904 .sourceIntTier = To IntensityTier (peaks)
911 .additiveBubblesSound = Create Sound: "Bubbles", 0, .targetDuration, .tagetSamplingFrequency, "0"
915 # Create an empty sound to receive the bubbles
916 .bubblesAudio = Read from file: .bubblesAudioName$
917 .bubblesTextGridName$ = replace_regex$(.bubblesAudioName$, "\.[a-z0-9]{2,}$", ".TextGrid", 0)
918 .bubblesTextGrid = Read from file: .bubblesTextGridName$
920 .sourceName$ = replace_regex$(selected$(), " ", "_", 0)
921 .bubblesSamplingFrequency = Get sampling frequency
922 .bubblesIntensity = Get intensity (dB)
923 .bubbleSound = Create Sound: "Bubbles", 0, .targetDuration, .bubblesSamplingFrequency, "0"
925 # Fill the new Bubbles
926 select .bubblesTextGrid
927 .numIntervals = Get number of intervals: 1
929 while .bubblesFound < .targetNumBubbles
930 .i = randomInteger(1, .numIntervals)
931 select .bubblesTextGrid
932 .label$ = Get label of interval: 1, .i
933 if .label$ = "sounding"
935 .startPoint = Get starting point: 1, .i
936 .endPoint = Get end point: 1, .i
937 .midPoint = (.startPoint + .endPoint)/2
938 .bubbleDuration = .endPoint - .startPoint
940 # Get random insertion point
941 .t = randomUniform (0.001, .targetDuration-0.001)
942 .targetStart = .t - .bubbleDuration/2
943 .targetEnd = .t + .bubbleDuration/2
945 Formula (part): .targetStart, .targetEnd, 1, 1, "self + '.sourceName$'((x - .t) + .midPoint)"
949 # Convert selected bubbles to scaled source
951 .resampledBubbleSound = Resample: .tagetSamplingFrequency, 50
953 .scaledBubbleSource = Multiply: "yes"
954 call set_VUV_to_zero .scaledBubbleSource .vuvTextGrid U
956 # The measured Intensity of the few selected bubbles can be too low. Correct for scaling
957 select .scaledBubbleSource
958 .bubbleSoundIntensity = Get intensity (dB)
959 .attenuation = .bubblesIntensity - .bubbleSoundIntensity
960 if .attenuation = undefined
964 # Scale bubble sounds
965 select .scaledBubbleSource
966 Scale intensity: .targetIntensity - .snr - .attenuation
968 select .scaledBubbleSource
970 .filteredBubbles = Filter: "no"
971 Rename: "FilteredBubbleNoise"
972 .additiveBubblesSound = Resample: .tagetSamplingFrequency, 50
975 select .resampledBubbleSound
976 plus .scaledBubbleSource
977 plus .filteredBubbles
981 plus .bubblesTextGrid
987 select .additiveBubblesSound
990 procedure add_single_bubble .sourceAudio .sourcePulses .sourceI .targetAudio .targetPulses .targetI
993 .sourceName$ = replace_regex$(selected$(), " ", "_", 0)
995 .targetName$ = replace_regex$(selected$(), " ", "_", 0)
999 .tTarget = Get time from index: .targetI
1000 .pLeft = Get interval: .tTarget - 0.001
1001 .pRight = Get interval: .tTarget + 0.001
1004 select .sourcePulses
1005 .tSource = Get time from index: .sourceI
1006 .qLeft = Get interval: .tSource - 0.001
1007 .qRight = Get interval: .tSource + 0.001
1009 # Gaussian window parameters (FWHM Left and Right)
1010 # FWHM = 2*sqrt(2*ln(2)) * c
1011 .c = (.qLeft+.qRight)/(2*sqrt(2*ln(2)))
1012 if not( .cL = undefined or .cR = undefined)
1015 Formula (part): .tTarget-.margin, .tTarget+.margin, 1, 1, "self + '.sourceName$'((x - .tTarget) + .tSource)*exp(-1*(((x - .tTarget)/.c)^2)/2)"