5 sentence Recorded_audio aa.wav
6 real Pitch 70 (= Hz, mean)
7 real Pitch_SD 15 (= % of mean)
8 real Duration 1.3 (= mult. factor)
10 real Bubbles 5 (= rate)
11 real Bubbles_SNR 10 (= dB SNR)
14 positive Voicing_floor_(dB) 15 (= below maximum)
18 ########################################################################
20 # VoiceConversion.praat
22 # Change the input speech to resemble Tracheoesophageal speech.
23 # Changes the Pitch (F0) and pitch movements, duration. Filtered noise
24 # is added as well as filtered "bubble" sounds.
25 # Increase the Jitter and Shimmer of a speech recording to the
26 # number given. Cannot reduce Jitter or Shimmer.
27 # Note that Jitter and Shimmer are ill-defined in anything but
30 # Uses the To PointProcess (periodic, cc) to calculate the jitter
31 # and To PointProcess (periodic, peaks): 60, 300, "yes", "yes"
32 # to change the timing of the periods.
34 # Periods are moved with Overlap-and-Add
36 # Shimmer is adapted using additive noise over an intensity tier and
37 # adapting each period individually. Periods are determined with the
38 # To PointProcess (periodic, peaks) pulses.
40 ########################################################################
42 # Copyright (C) 2016-2017 NKI-AVL, R. J. J. H. van Son
45 # This program is free software: you can redistribute it and/or modify
46 # it under the terms of the GNU General Public License as published by
47 # the Free Software Foundation, either version 3 of the License, or
48 # (at your option) any later version.
50 # This program is distributed in the hope that it will be useful,
51 # but WITHOUT ANY WARRANTY; without even the implied warranty of
52 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
53 # GNU General Public License for more details.
55 # You should have received a copy of the GNU General Public License
56 # along with this program. If not, see <http://www.gnu.org/licenses/>.
58 # Full license text is available at:
59 # http://www.gnu.org/licenses/gpl-3.0.html
61 ########################################################################
63 # Input parameters (<=0 means "do not change"):
65 # Input file A file name (with full path). If a Sound object is selected, that will be used instead
66 # Pitch Average pitch of the new speech in Hz [F'(t) = Fnew/Fold * F(t)]
67 # Pitch SD Standard deviation of the Pitch of the new speech in Hz (compresses pitch movements)
68 # [SD'(t) = SDnew/SDold * (F(t) - Faverage) + Faverage]
69 # Duration Factor with which to multiply the duration
70 # HNR Signal to Noise ratio of new noise added to obtain the HNR given
71 # Bubbles Rate of bubble sounds added (per second). Select random bubbles from bubbles.wav&bubbles.TextGrid
72 # Bubbles SNR Signal to Noise ratio of bubble sounds added (use bubbles.wav)
73 # Jitter New jitter in %
74 # Shimmer New Shimmer in %
75 # Voicing floor Lowest level of sound still considered voiced, in dB below the maximum
77 # Help Print this text and exit
80 # The input sound converted according to the specifications
82 # Print debugging information
87 # A Praat Sound object with the transformed speech
90 # praat VoiceConversion.praat Speech/Example1.wav 70 15 1.3 5 5 15 5 10 15 no
98 printline Input parameters (<=0 means "do not change"):
99 printline Input file'tab$''tab$'A file name (with full path). If a Sound object is selected, that will be used instead
100 printline Pitch'tab$''tab$''tab$'Average pitch of the new speech in Hz [F'(t) = Fnew/Fold * F(t)]
101 printline Pitch SD'tab$''tab$'Standard deviation of the Pitch of the new speech in Hz (compresses pitch movements)
102 printline 'tab$''tab$''tab$''tab$'[SD'(t) = SDnew/SDold * (F(t) - Faverage) + Faverage]
103 printline Duration'tab$''tab$'Factor with which to multiply the duration
104 printline HNR'tab$''tab$''tab$''tab$'Signal to Noise ratio of new noise added to obtain the HNR given
105 printline Bubbles'tab$''tab$''tab$'Rate of bubble sounds added (per second). Select random bubbles from bubbles.wav&bubbles.TextGrid
106 printline Bubbles SNR'tab$''tab$'Signal to Noise ratio of bubble sounds added (use bubbles.wav)
107 printline Jitter'tab$''tab$''tab$'New jitter in %
108 printline Shimmer'tab$''tab$''tab$'New Shimmer in %
109 printline Voicing floor'tab$'Lowest level of sound still considered voiced, in dB below the maximum
111 printline Help'tab$''tab$''tab$'Print this text and exit
114 printline The input sound converted according to the specifications
121 if numberOfSelected("Sound") > 0
122 .recordedSound = selected("Sound")
123 elsif recorded_audio$ <> "" and fileReadable(recorded_audio$)
124 .recordedSound = Read from file: recorded_audio$
125 Rename: "RecordedSpeech"
128 bubblesAudioName$ = "bubbles.wav"
130 te_source_bubbles_name$ = "TE_source_bubbles.wav"
132 .thresshold = -voicing_floor
135 select .recordedSound
136 global.setIntensity = Get intensity (dB)
138 call convert_speechOverlapAndAdd .recordedSound .thresshold jitter shimmer pitch pitch_SD duration hNR bubbles bubbles_SNR
140 # Definitions of functions
143 procedure convert_speechOverlapAndAdd .recordedSound .thresshold .jitter .shimmer .pitch .pitch_SD .durationFactor .newHNR .bubble_rate .bubble_snr
144 call change_pitch_duration .recordedSound .pitch .pitch_SD .durationFactor
145 .newPitchSound = selected("Sound")
147 call extractVoicingParameters .newPitchSound .thresshold
148 .recordedTextGrid = selected("TextGrid")
149 .recordedPulses = selected("PointProcess")
150 .recordedInt = selected("Intensity")
152 select .newPitchSound
153 .recordedPulsesPeaks = To PointProcess (periodic, peaks): 60, 300, "yes", "yes"
155 call create_additive_noise .newPitchSound .newHNR .recordedTextGrid
156 .additiveNoise = selected("Sound")
158 call add_bubbles '.newPitchSound' '.bubble_rate' '.bubble_snr' '.recordedTextGrid' 'bubblesAudioName$'
159 .additiveBubbles = selected("Sound")
161 # Change Jitter, use CC to determine jitter and Peaks to change the periods
162 selectObject: .recordedPulsesPeaks
163 .newPointProcess = Copy: "New_Pulses"
164 call set_jitter .jitter .newPointProcess .recordedPulses
165 call test_overlap_add .newPitchSound .recordedPulsesPeaks .recordedTextGrid .newPointProcess .shimmer
166 .newSound = selected("Sound")
168 # Create bubbles from source
169 call add_source_bubbles '.newPitchSound' '.recordedPulses' 0.7 10 'te_source_bubbles_name$'
174 selectObject: .recordedPulses
175 .old_jitter = Get jitter (local): 0, 0, 0.0001, 0.03, 2
176 selectObject: .newPointProcess
177 .newPPjitter = Get jitter (local): 0, 0, 0.0001, 0.03, 2
178 selectObject: .recordedSound
180 .old_amplitude = To AmplitudeTier (period): 0, 0, 0.0001, 0.03, 2
181 .old_shimmer = Get shimmer (local): 0.0001, 0.03, 2
184 selectObject: .newSound
185 .pointP = To PointProcess (periodic, cc): 60, 300
186 .new_jitter = Get jitter (local): 0, 0, 0.0001, 0.03, 2
188 selectObject: .newSound
190 .new_amplitude = To AmplitudeTier (period): 0, 0, 0.0001, 0.03, 2
191 .new_shimmer = Get shimmer (local): 0.0001, 0.03, 2
193 appendInfoLine: "New Jitter: '.new_jitter:1%' ('.old_jitter:1%' ~> '.newPPjitter:1%')"
194 appendInfoLine: "New Shimmer: '.new_shimmer:1%' ('.old_shimmer:1%')"
196 selectObject: .old_amplitude, .pointP, .new_amplitude
200 # Add noise to result
201 call add_sounds .newSound .additiveNoise
202 .resultNoise = selected("Sound")
205 # Add bubbles to result
206 call add_sounds .resultNoise .additiveBubbles
207 .result = selected("Sound")
211 selectObject: .newPitchSound, .recordedTextGrid, .recordedPulses, .recordedInt, .newPointProcess, .recordedPulsesPeaks, .newSound, .additiveNoise, .additiveBubbles, .resultNoise
214 selectObject: .result
217 procedure change_pitch_duration .sound .pitch .pitchFraction .durationFactor
219 .duration = Get total duration
221 .manipulation = To Manipulation: 0.01, 70, 300
222 .pitchTier = Extract pitch tier
223 .currentPitch = Get mean (points): 0, 0
224 .pitch_SD = .pitchFraction / 100 * .pitch
227 .durationTier = Extract duration tier
230 if .durationFactor > 0
231 .numPoints = Get number of points
235 Formula: "self*'.durationFactor'"
238 Replace duration tier
243 .factor = (.pitch / .currentPitch)
244 Multiply frequencies: 0, .duration, .factor
245 .currentSD = Get standard deviation (points): 0, 0
248 .factor = .pitch_SD / .currentSD
249 Formula: "'.pitch' + (self - '.pitch') * '.factor'"
258 if .currentPitch > 0 or .durationFactor > 0
260 .newSound = Get resynthesis (overlap-add)
263 .newSound = Copy: "New Sound"
273 procedure extractVoicingParameters .recordedSound .thresshold
274 # The lowest level of voiced sounds
275 select .recordedSound
276 .pointPcc = To PointProcess (periodic, cc): 60, 300
277 Rename: "RecordedPulses"
278 .textGrid = To TextGrid (vuv): 0.02, 0.01
279 Rename: "RecordedVoicing"
280 .numIntervals = Get number of intervals: 1
282 # Correct voicing boundaries
283 select .recordedSound
284 .intensity = To Intensity: 100, 0, "yes"
285 Rename: "RecordedIntensity"
286 .silences = To TextGrid (silences): .thresshold, 0.1, 0.05, "silent", "sounding"
289 for .i to .numIntervals
291 .label$ = Get label of interval: 1, .i
293 .start = Get starting point: 1, .i
294 .end = Get end point: 1, .i
296 # Starting point of voiced interval
298 .s = Get interval at time: 1, .start
299 .sLabel$ = Get label of interval: 1, .s
300 if .sLabel$ = "silent"
301 .sStart = Get starting point: 1, .s
302 .sEnd = Get end point: 1, .s
305 Set interval text: 1, .i, "U"
306 # Shift boundaries: Insert&Remove
307 Insert boundary: 1, .sEnd
308 Set interval text: 1, .i+1, "V"
310 Set interval text: 1, .i, ""
311 Remove left boundary: 1, .i
314 # Low intensity, unvoiced
315 Set interval text: 1, .i, "U"
322 for .i to .numIntervals
324 .label$ = Get label of interval: 1, .i
326 .start = Get starting point: 1, .i
327 .end = Get end point: 1, .i
329 # Starting point of voiced interval
331 .s = Get interval at time: 1, .end
332 .sLabel$ = Get label of interval: 1, .s
333 if .sLabel$ = "silent"
334 .sStart = Get starting point: 1, .s
335 .sEnd = Get end point: 1, .s
338 Set interval text: 1, .i, "U"
339 # Shift boundaries: Insert&Remove
340 Insert boundary: 1, .sStart
341 Set interval text: 1, .i, "V"
343 Set interval text: 1, .i+1, ""
344 Remove right boundary: 1, .i+1
347 # Low intensity, unvoiced
348 Set interval text: 1, .i, "U"
362 procedure create_additive_noise .sound .newHNR .vuvTextGrid
364 .duration = Get total duration
365 .sampleFreq = Get sampling frequency
367 .additiveNoiseSound = -1
369 # Determine noise level
371 .originalIntensity = Get intensity (dB)
372 .additiveNoise = .originalIntensity - .newHNR
376 .downsampled = Resample: 10000, 50
377 .lpc = To LPC (autocorrelation): 10, 0.025, 0.005, 50
379 .source = Filter (inverse)
380 .sourceInt = To Intensity: 70, 0, "yes"
381 .sourceIntTier = To IntensityTier (peaks)
383 # Create additive noise
384 if .additiveNoise > 0
385 .noise = Create Sound from formula: "WhiteNoise", 1, 0, .duration, .sampleFreq, "randomGauss(0,0.1)"
387 .filteredNoise = Filter: "no"
389 .additiveNoiseSoundTMP = Multiply: "yes"
390 call set_VUV_to_zero .additiveNoiseSoundTMP .vuvTextGrid U
391 Scale intensity: .additiveNoise
392 .additiveNoiseSound = Resample: .sampleFreq, 50
394 selectObject: .noise, .filteredNoise, .additiveNoiseSoundTMP
398 selectObject: .downsampled, .lpc, .source, .sourceInt, .sourceIntTier
403 if .additiveNoiseSound <= 0
404 .additiveNoiseSound = Create Sound from formula: "AdditiveNoise", 1, 0, .duration, .sampleFreq, "0"
407 select .additiveNoiseSound
410 procedure add_sounds .sound1 .sound2
411 selectObject: .sound1, .sound2
412 .stereo = Combine to stereo
413 .addedSound = Convert to mono
418 selectObject: .addedSound
422 # Set Jitter to a specified number
424 # Ti = ti - ti-1 (interval i)
425 # Jitter (absolute) is Sum[ abs(Ti - Ti-1) ] / N-1
426 # Jitter = Jitter (absolute) / mean(Ti)
428 # For a Normal distribution
429 # E(|X|) = sqrt(2/pi) * stdev(X)
432 # E(Ti^2) = var(Ti) + E(Ti)^2
433 # E(Ti*Ti-1) = cor(Ti, Ti-1) + E(Ti)^2
434 # var(Ti - Ti-1) = E(Ti^2 - 2*Ti*Ti-1 + Ti-1^2)
435 # = 2*E(Ti^2) - 2*E(Ti*Ti-1)
436 # = 2*[ var(Ti) * (1 - cor(Ti, Ti-1)) ]
438 # Combine, assuming a Normal distribution:
439 # Jitter = E(|Ti - Ti-1|) / E(Ti)
440 # = sqrt(2/pi) * stdev(Ti - Ti-1) / mean(Ti)
441 # = sqrt(2/pi * var(Ti - Ti-1)) / mean(Ti)
442 # = sqrt[ 4/pi * ( var(Ti) * (1 - cor(Ti, Ti-1)) ) ] / mean(Ti)
444 # Change Ti -> T'i; Jitter -> a*Jitter while keeping mean(Ti) = mean(T'i) constant
445 # ei = (ti + ti-2)/2 - ti-1
446 # Jitter' = a * Jitter
447 # = a * sqrt[ 4/pi * var(Ti - Ti-1) ] / mean(Ti)
449 # => var(T'i - T'i-1) = a^2 * var(Ti - Ti-1)
450 # = a^2 * E[ (Ti - Ti-1)^2 ]
451 # = a^2 * E[ (ti - ti-1 - ti-1 + ti-2)^2 ]
452 # = a^2 * 2 * E[ ((ti + ti-2)/2 - ti-1)^2 ]
453 # = a^2 * 2 * E[ ei^2 ]
454 # = 2 * E[ (a*ei)^2 ]
457 # Generalizing, var(T'i - T'i-1) = 2*(var(ti-1) + var(ti) + var(ti+1))
458 # To increase Jitter -> Jitter'
459 # 1) Determine var(Ti - Ti-1) = (Jitter * mean(Ti))^2 * pi / 2
460 # 2) Calculate var(T'i - T'i-1) = (Jitter' * mean(T'i))^2 * pi / 2
461 # 3) Determine var to add:
462 # add_var(Ti - Ti-1) = var(T'i - T'i-1) - var(Ti - Ti-1)
463 # 4) Var of Noise to add per ti: add_var(ti) = add_var(Ti - Ti-1)/(2*3)
464 # 5) Sd of Noise to add per ti: add_sd(ti) = sqrt(add_var(ti))
467 # Converts .pulses into pulses with new Jitter
468 procedure set_jitter .newJitter .pulses .pulsesCC
470 if .pulses > 0 and .newJitter > 0
473 # Use CC to determine real jitter
477 .current_jitter = Get jitter (local): 0, 0, 0.0001, 0.03, 2
478 .current_abs_jitter = Get jitter (local, absolute): 0, 0, 0.0001, 0.03, 2
479 .current_mean_period = Get mean period: 0, 0, 0.0001, 0.03, 2
480 .current_stdev_period = Get stdev period: 0, 0, 0.0001, 0.03, 2
482 if .newJitter > .current_jitter
483 .current_var = .current_abs_jitter**2 * pi/2
484 .end_var = (.newJitter * .current_mean_period)**2 * pi/2
485 # The variance to add per boundary (total / (2*3))
486 .add_var = (.end_var - .current_var) / 6
487 .stdev_e = sqrt(.add_var)
489 # Keep the original pulses just is case the order of the pulses might change
491 .origPulses = Copy: "Original_Pulses"
492 .numPoints = Get number of points
495 # Change jitter by moving the ti according to
496 # t'i = ti - randomGauss(0, stdev(e'))
497 for .p from 1 to .numPoints
499 .t = Get time from index: .p
500 .new_t = .t - randomGauss(0, .stdev_e)
502 # Remove current point
504 .r = Get nearest index: .t
512 pause New jitter: '.newJitter' must be larger than current jitter '.current_jitter:4'
515 # Calculate new jitter
517 .jitter_new = Get jitter (local): 0, 0, 0.0001, 0.03, 2
519 .current_jitter *= 100
526 # We cannot use the shimmer of a sentence, so we can only "add" shimmer
528 # .new_shimmer is in %
529 # .sound: Source Sound
530 # .pulses: PointProcess
531 # .voicing: VUV TextGrid
532 # .new_shimmer: New shimmer in %
533 procedure increase_shimmer .sound .pulses .voicing .newShimmer
538 # Create Amplitude Tier and get current shimmer
540 .duration = Get total duration
542 .current_amplitude = To AmplitudeTier (period): 0, 0, 0.0001, 0.03, 2
543 .current_shimmer = Get shimmer (local): 0.0001, 0.03, 2
544 select .current_amplitude
545 .numPoints = Get number of points
546 .ampreal = Down to TableOfReal
549 for .p from 1 to .numPoints
551 .tmp = Get value: .p, 2
557 .meanAmp = .sumamp / .n
559 # Sd must be multiplied with the amplitude
560 if .newShimmer > .current_shimmer
561 .new_var = (.newShimmer**2 - .current_shimmer**2) * .meanAmp**2 * pi / 2
563 .new_var = .newShimmer**2 * .meanAmp**2 * pi / 2
566 .new_sd = sqrt(.new_var / 2)
571 .new_amplitude = Create AmplitudeTier: "New_Amplitude", 0, .duration
572 for .p from 1 to .numPoints
574 .t = Get value: .p, 1
575 .a = Get value: .p, 2
580 .new_a = .a - randomGauss(0, .new_sd)
586 select .new_amplitude
587 Add point: .t, .new_a / .a
593 # Set unvoiced parts to 1
594 select .new_amplitude
598 .numIntervals = Get number of intervals: 1
599 for .i from 1 to .numIntervals
601 .t = Get end point: 1, .i
602 select .new_amplitude
607 plus .current_amplitude
610 # Overlay shimmer over sound
613 .new_sound = Multiply
614 Rename: "NewSound_Shimmer"
618 .shimmer_new = Get shimmer (local): 0, 0, 0.0001, 0.02, 1.3, 1.6
620 .current_shimmer *= 100
623 select .new_amplitude
628 .new_sound = Copy: "NewSound_Shimmer"
634 # Make a copy of the source to the target matching the pulses in source and target
635 # Copies fragments around pulses in sourcePulses under the direction of the
636 # corresponding pulses in targetPulses using the Overlap&Add method (Gaussian window)
638 # Ignores voiceless parts, ie, intervals between pulses > .maxInt
639 # For voices, .maxInt should be ~0.02 (F0 > 50Hz). For other sounds, e.g., bubbles, this
640 # should be increased to fit the whole sound between pulses.
642 # Midpoint between the pulses, periods add up to a factor of ~1.04.
643 # At the pulses themselves, it adds up to ~1.12 (summed left and right)
645 procedure overlap_add .sourceSound .sourcePulses .targetSound .targetPulses .maxInt
646 # Create empty .targetSound if .targetSound does not exist
649 .duration = Get total duration
650 .samplingFrequency = Get sampling frequency
651 .targetSound = Create Sound from formula: "Target Sound", 1, 0, .duration, .samplingFrequency, "0"
653 # Default, just copy the source pulses
654 if .targetPulses <= 0
655 .targetPulses = .sourcePulses
658 # Maximum interval between pulses (maximum pitch period)
664 .sourceName$ = replace_regex$(selected$(), " ", "_", 0)
666 .targetName$ = replace_regex$(selected$(), " ", "_", 0)
668 # Iterate over target pulses
670 .numPulses = Get number of points
674 .tTarget = Get time from index: .p
675 .pLeft = Get interval: .tTarget - 0.001
676 .pRight = Get interval: .tTarget + 0.001
679 .q = Get nearest index: .tTarget
680 .tSource = Get time from index: .q
681 .qLeft = Get interval: .tSource - 0.001
682 .qRight = Get interval: .tSource + 0.001
683 # Gaussian window parameters (FWHM Left and Right)
684 # FWHM = 2*sqrt(2*ln(2)) * c
685 .cL = min(.pLeft,.qLeft)/(2*sqrt(2*ln(2)))
686 .cR = min(.pRight,.qRight)/(2*sqrt(2*ln(2)))
687 if not( .cL = undefined or .cL > .maxInt/(2*sqrt(2*ln(2))) or .cR = undefined or .cR > .maxInt/(2*sqrt(2*ln(2))) )
690 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"
698 procedure test_overlap_add .sourceAudio .sourcePulses .vuvTextGrid .targetPulses .newShimmer
699 # Use overlap-add to add new source intervals
700 # Copy only voiced pulses
701 call set_VUV_to_zero .targetPulses .vuvTextGrid U
702 # Create a copy of the old source with voiced parts zeroed
704 .testSource = Copy: "OaAsound"
705 call set_VUV_to_zero .testSource .vuvTextGrid V
707 # Copy the voiced parts of the new source to the zeroed voiced parts of the old source
708 call overlap_add .sourceAudio .sourcePulses .testSource .targetPulses 0.02
709 call increase_shimmer .testSource .targetPulses .vuvTextGrid .newShimmer
710 .newSound = selected("Sound")
711 Scale intensity: global.setIntensity
720 # Set intervals matching a label text to Zero or remove the pulses
721 # Works on Sound and Pulses
722 procedure set_VUV_to_zero .sound .vuvTextGrid .zeroLabel$
724 .objectType$ = selected$()
725 .objectType$ = extractWord$ (.objectType$, "")
727 .numIntervals = Get number of intervals: 1
728 # Zero out VU intervals
729 for .i to .numIntervals
731 .vuvLabel$ = Get label of interval: 1, .i
732 .start = Get starting point: 1, .i
733 .end = Get end point: 1, .i
734 if .vuvLabel$ = .zeroLabel$
736 if .objectType$ = "Sound"
737 Set part to zero: .start, .end, "at nearest zero crossing"
738 elsif .objectType$ = "PointProcess"
739 Remove points between: .start, .end
741 printline Unsupported object type for set_VUV_to_zero
750 # Create a new Bubbles source file from an existing source file
752 # Uses only a randomly selected fraction of the source pulses
754 procedure create_source_bubbles .sourceSound .sourcePulses .fraction
755 # Normalize fraction: <0, 1]. values > 1 are considered %
758 elsif .fraction > 1 and .fraction <= 100
759 .fraction = .fraction / 100
760 elsif .fraction <= 1000
761 .fraction = .fraction / 1000
768 .targetPulses = Copy: "Target Pulses"
770 .numPulses = Get number of points
771 .discard = 1 - .fraction
773 .rand = randomUniform (0, 1)
781 call overlap_add '.sourceSound' '.sourcePulses' -1 '.targetPulses' 0.02
782 .targetSound = overlap_add.targetSound
789 procedure add_source_bubbles .sound .pulses .fraction .snr .bubblesAudioName$
792 .duration = Get total duration
793 .targetIntensity = Get intensity (dB)
794 .targetDuration = Get total duration
795 .tagetSamplingFrequency = Get sampling frequency
796 .downsampled = Resample: 10000, 50
797 .lpc = To LPC (autocorrelation): 10, 0.025, 0.005, 50
799 .source = Filter (inverse)
800 .sourceInt = To Intensity: 70, 0, "yes"
801 .sourceIntTier = To IntensityTier (peaks)
807 # Create the taget file
808 .masterSourceSound = Read from file: .bubblesAudioName$
809 .masterDuration = Get total duration
810 while .masterDuration < 2*.duration
811 .tmpA = .masterSourceSound
814 .masterSourceSound = Concatenate
815 selectObject: .tmpA, .tmpB
817 select .masterSourceSound
818 .masterDuration = Get total duration
820 # Get a random start point
821 .startPoint = randomUniform (0, .duration)
822 select .masterSourceSound
823 .sourceSound = Extract part: .startPoint, .startPoint+.duration, "rectangular", 1, "no"
824 select .masterSourceSound
826 # There is a sourceSound of the correct length, copy a fraction of the pulses
827 call create_source_bubbles .sourceSound .pulses .fraction
828 .targetSound = create_source_bubbles.targetSound
832 .scaledBubbleSource = Multiply: "yes"
833 Scale intensity: .targetIntensity - .snr
835 selectObject: .targetSound, .sourceIntTier, .sourceSound
838 select .scaledBubbleSource
840 .filteredBubbleSource = Filter: "no"
841 Rename: "TargetBubbleSound"
842 .targetSound = Resample: .tagetSamplingFrequency, 50
844 selectObject: .scaledBubbleSource, .lpc, .filteredBubbleSource
848 Scale intensity: .targetIntensity - .snr
854 # Select a random puls in the bubbles and add it to a random puls in the target
856 # Creates a sound with only the bubbles
858 procedure add_bubbles .sound .rate .snr .vuvTextGrid .bubblesAudioName$
861 .targetIntensity = Get intensity (dB)
862 .targetDuration = Get total duration
863 .tagetSamplingFrequency = Get sampling frequency
864 .targetNumBubbles = .rate * .targetDuration
865 .downsampled = Resample: 10000, 50
866 .lpc = To LPC (autocorrelation): 10, 0.025, 0.005, 50
868 .source = Filter (inverse)
869 .sourceInt = To Intensity: 70, 0, "yes"
870 .sourceIntTier = To IntensityTier (peaks)
877 .additiveBubblesSound = Create Sound: "Bubbles", 0, .targetDuration, .tagetSamplingFrequency, "0"
881 # Create an empty sound to receive the bubbles
882 .bubblesAudio = Read from file: .bubblesAudioName$
883 .bubblesTextGridName$ = replace_regex$(.bubblesAudioName$, "\.[a-z0-9]{2,}$", ".TextGrid", 0)
884 .bubblesTextGrid = Read from file: .bubblesTextGridName$
886 .sourceName$ = replace_regex$(selected$(), " ", "_", 0)
887 .bubblesSamplingFrequency = Get sampling frequency
888 .bubblesIntensity = Get intensity (dB)
889 .bubbleSound = Create Sound: "Bubbles", 0, .targetDuration, .bubblesSamplingFrequency, "0"
891 # Fill the new Bubbles
892 select .bubblesTextGrid
893 .numIntervals = Get number of intervals: 1
895 while .bubblesFound < .targetNumBubbles
896 .i = randomInteger(1, .numIntervals)
897 select .bubblesTextGrid
898 .label$ = Get label of interval: 1, .i
899 if .label$ = "sounding"
901 .startPoint = Get starting point: 1, .i
902 .endPoint = Get end point: 1, .i
903 .midPoint = (.startPoint + .endPoint)/2
904 .bubbleDuration = .endPoint - .startPoint
906 # Get random insertion point
907 .t = randomUniform (0.001, .targetDuration-0.001)
908 .targetStart = .t - .bubbleDuration/2
909 .targetEnd = .t + .bubbleDuration/2
911 Formula (part): .targetStart, .targetEnd, 1, 1, "self + '.sourceName$'((x - .t) + .midPoint)"
915 # Convert selected bubbles to scaled source
917 .resampledBubbleSound = Resample: .tagetSamplingFrequency, 50
919 .scaledBubbleSource = Multiply: "yes"
920 call set_VUV_to_zero .scaledBubbleSource .vuvTextGrid U
922 # The measured Intensity of the few selected bubbles can be too low. Correct for scaling
923 select .scaledBubbleSource
924 .bubbleSoundIntensity = Get intensity (dB)
925 .attenuation = .bubblesIntensity - .bubbleSoundIntensity
926 if .attenuation = undefined
930 # Scale bubble sounds
931 select .scaledBubbleSource
932 Scale intensity: .targetIntensity - .snr - .attenuation
934 select .scaledBubbleSource
936 .filteredBubbles = Filter: "no"
937 Rename: "FilteredBubbleNoise"
938 .additiveBubblesSound = Resample: .tagetSamplingFrequency, 50
941 select .resampledBubbleSound
942 plus .scaledBubbleSource
943 plus .filteredBubbles
947 plus .bubblesTextGrid
953 select .additiveBubblesSound
956 procedure add_single_bubble .sourceAudio .sourcePulses .sourceI .targetAudio .targetPulses .targetI
959 .sourceName$ = replace_regex$(selected$(), " ", "_", 0)
961 .targetName$ = replace_regex$(selected$(), " ", "_", 0)
965 .tTarget = Get time from index: .targetI
966 .pLeft = Get interval: .tTarget - 0.001
967 .pRight = Get interval: .tTarget + 0.001
971 .tSource = Get time from index: .sourceI
972 .qLeft = Get interval: .tSource - 0.001
973 .qRight = Get interval: .tSource + 0.001
975 # Gaussian window parameters (FWHM Left and Right)
976 # FWHM = 2*sqrt(2*ln(2)) * c
977 .c = (.qLeft+.qRight)/(2*sqrt(2*ln(2)))
978 if not( .cL = undefined or .cR = undefined)
981 Formula (part): .tTarget-.margin, .tTarget+.margin, 1, 1, "self + '.sourceName$'((x - .tTarget) + .tSource)*exp(-1*(((x - .tTarget)/.c)^2)/2)"