# AdjustDurPitch.praat # # Script for PRAAT 5.1 (www.praat.org) # # This script takes two Sounds and two associated TextGrids as input. # It adjusts the segment durations of the first* Sound, to the relative # durations of the corresponding segments in the second Sound. # The resulting duration for the manipulated sentence is intermediate between the # sentence durations of the two input sentences. # The script assumes that the TextGrids have the same number of intervals and # the same labels within each interval, and it issues warnings if there is a mismatch. # This script also assumes that the first interval and the last interval # have no label, and contain no speech sound. The corresponding parts of # sound1 will not be changed in duration. # # * Note: The "first" Sound refers to the first Sound in the list of selected objects # in the Praat objects window, and it does NOT refer to the first Sound # that was added to the selection of objects. # This means that if you want to reverse the direction of adjustement, you have to # make sure that the Sound objects are in reverse order in the list of objects in # the Praat objects window. # # # This Praat script was written for the thesis work of Lisette van Delft. # # (c) 2009 Hugo Quené, Utrecht University [H.Quene@uu.nl] # HQ 20090210 initial release # HQ 20090309 added optional transposition of intonation # The resulting pitch contour takes the pitch points from the second Sound, # shifted so that their average is equal to the average pitch of the first Sound. # The pitch points are then copied to the first Sound, so that the pitch points # keep their relative timing position within each segment. # HQ 20090310 removed bug in computation of sent2d # # This script requires that two Sound objects and two TextGrid objects are # selected when the script is called. if ( numberOfSelected("Sound") != 2 or numberOfSelected("TextGrid") != 2 ) exit Exactly two Sound and two (associated) TextGrid objects must be selected. endif # set working directory for read and write indir$ = "~/rhythm/Lisette_van_Delft/" # blank line in console window # printline # two Sound and associated TextGrid must be selected # sent1 is the original to be manipulated sent1id = selected("Sound",1) sent1$ = selected$("Sound",1) textgrid1id = selected("TextGrid",1) # sent2 is the target model to use for duration and pitch sent2id = selected("Sound",2) sent2$ = selected$("Sound",2) textgrid2id = selected("TextGrid",2) # ask interactively for "minimum pitch" and "silence threshold" values # minpitch = 50 # threshold = -21 form Analysis parameters # comment Sound 'sent1$' will be modified to temporal pattern in 'sent2$' # comment _ comment Erase the Info window? # comment (This will permanently remove all contents from the Info window). boolean erase_Info_window 0 comment Replace pitch contour with modified pitch? boolean replace_pitch 1 # comment _ positive minimum_pitch_(Hz) 70 positive maximum_pitch_(Hz) 300 endform # granularity of duration points, shortest distance between points shortperiod = 1/'maximum_pitch' if erase_Info_window = 1 # erase entire console window clearinfo endif printline Processing Sound 'sent1$'... # this script requires that the two textgrids are identical in their labels, and # that they differ only in the boundaries of the intervals in the first tier. # in addition this script assumes that the first interval and the last interval # have no label, and contain no speech sound. The corresponding parts of # sound1 will not be changed in duration. select 'textgrid1id' sent1t = Get total duration sent1ns = Get number of intervals... 1 sent1b = Get end point... 1 1 sent1e = Get start point... 1 'sent1ns' sent1d = 'sent1e' - 'sent1b' select 'textgrid2id' sent2t = Get total duration sent2ns = Get number of intervals... 1 sent2b = Get end point... 1 1 sent2e = Get start point... 1 'sent2ns' sent2d = 'sent2e' - 'sent2b' # check whether numbers of intervals are equal if sent1ns != sent2ns printline Error: unequal numbers of segments ('sent1$': 'sent1ns:0'; 'sent2$': 'sent2ns:0') exit endif # The resulting duration of the speech portion should be intermediate # between sent1d and sent2d. So all new durations should be multiplied # by a scalar factor, which takes the difference in duration, halves the difference, # and adds that to the original duration. the resulting new duration for the speech # portion is finally expressed as a proportion relative to the old duration. targetdur = sent1d + ((sent2d-sent1d)/2) scalar = targetdur / sent1d printline current sentence duration is 'sent1d:3' s ('sent2$' is 'sent2d:3' s); printline new duration is 'targetdur:3' (scale factor 'scalar:5') # extract pitch information of sent1 before deleting pitch info if replace_pitch = 1 # extract pitch contour of sound to adjust select 'sent1id' To Pitch... 0 'minimum_pitch' 'maximum_pitch' pitch1tempid = selected("Pitch") Kill octave jumps pitch1id = selected("Pitch") # NOT: To PitchTier Down to PitchTier # stylization only possible on PitchTier, not on Pitch Stylize... 2 Semitones pitchtier1id = selected("PitchTier") averpitch1 = Get mean (points)... 0 0 # extract pitch contour of target sound select 'sent2id' To Pitch... 0 'minimum_pitch' 'maximum_pitch' pitch2tempid = selected("Pitch") Kill octave jumps pitch2id = selected("Pitch") # NOT: To PitchTier Down to PitchTier # stylization only possible on PitchTier, not on Pitch Stylize... 2 Semitones pitchtier2id = selected("PitchTier") averpitch2 = Get mean (points)... 0 0 printline pitchtier average (points): 'sent1$' 'averpitch1:3'; 'sent2$': 'averpitch2:3' endif # create Manipulation object select 'sent1id' manip1id = To Manipulation... 0.010 'minimum_pitch' 'maximum_pitch' manip1$ = selected$("Manipulation") Edit editor Manipulation 'sent1$' Set duration range... 0.25 2.5 if replace_pitch = 1 # remove all pitch points Select... 0 'sent1t' Remove pitch point(s) # later new points will be added based on pitchtier2 else Stylize pitch (2 st) endif endeditor cumdur = 0 if replace_pitch = 1 select 'pitchtier2id' # shift frequencies in pitchtier2 to average of pitch1 f0diff = averpitch1-averpitch2 Shift frequencies... 0 0 'f0diff' Hertz # resulting pitchtier2 should have same average as pitchtier1 printline pitch shifted: in 'sent2$' by 'f0diff:3' Hz pp2np = Get number of points endif for i from 1 to 'sent1ns' # check whether labels are identical ? select 'textgrid1id' seg1b = Get start point... 1 'i' seg1e = Get end point... 1 'i' seg1d = seg1e-seg1b lab1$ = Get label of interval... 1 'i' select 'textgrid2id' seg2b = Get start point... 1 'i' seg2e = Get end point... 1 'i' seg2d = seg2e-seg2b lab2$ = Get label of interval... 1 'i' if lab1$ != lab2$ printline Warning: Sound 'sent1$', segment 'i', labels do not match ('lab1$' vs 'lab2$') endif newdur = 'seg1d' if i=1 factor=1 elsif i='sent1ns' factor=1 else # 1. factor is proportion of segment in selected target sentence factor = (seg2d/sent2d) # 2. multiplied by NEW target duration, yields new segment duration newdur = 'factor' * 'targetdur' # 3. factor is new segment duration divided by existing segment duration factor = 'newdur' / 'seg1d' cumdur = cumdur + newdur endif # printline Segment 'i', 'seg1d:3' s (target 'seg2d:3', sent2 'sent2d:3' s), new 'newdur:3', factor 'factor:5' call adddurpoints 'manip1id' 'manip1$' 'seg1b' 'seg1e' 'factor' # drastic PSOLA manipulation may yield strange artefacts, see book Dutoit if factor > 2.0 printline Warning: Sound 'sent1$', segment 'i' ('lab1$'), factor 'factor:5' may be too large elsif factor < 0.5 printline Warning: Sound 'sent1$', segment 'i' ('lab1$'), factor 'factor:5' may be too small endif if replace_pitch = 1 # adjust pitch points within this segment # get first pitch point (pp) after seg2b select 'pitchtier2id' pp2 = Get high index from time... 'seg2b' # we could be past the last pitch point in sent2, so pp2 is undefined # which would lead to failure later on if pp2 = undefined ppt2 = 'sent2t' printline No pitch point found in segment 'pp2' or later else ppt2 = Get time from index... 'pp2' endif while ppt2 < seg2e # while pitchpoint pp has time ppt that is before seg2e select 'pitchtier2id' # printline * pp2='pp2' 'ppt2:3' 'seg2b:3' 'seg2e:3' 'seg2d:3' f0value = Get value at index... 'pp2' # get relative position in segment seg2, as proportion relatime = (ppt2-seg2b) / (seg2d) # add pitch point on pitch tier in manipulation object # printline * adding... 'mid' 'mani$' 'seg1b:3' 'seg1e:3' 'relatime:3' 'f0value:0' call addpitchpoint 'mid' 'mani$' 'seg1b' 'seg1e' 'relatime' 'f0value' # goto next pitch point if pp2 < pp2np # current point could be the last pitch point pp2 = 'pp2' + 1 # printline * moving on to pp2='pp2' select 'pitchtier2id' ppt2 = Get time from index... 'pp2' else # make sure that while loop exits (because while loop is false) # by setting ppt2 to point later than seg2e ppt2 = 'sent2t' endif endwhile endif endfor procedure adddurpoints mid mani$ s1b s1e fact # add two duration points, in manipulation object passed as parameters, # at positions (just after) s1b and (just before) s1e, of value fact select 'mid' s1b = s1b+(shortperiod/2) s1e = s1e-(shortperiod/2) # no need to open new ManipulationEditor window, use existing one # Edit editor Manipulation 'mani$' Add duration point at... 's1b' 'fact' Add duration point at... 's1e' 'fact' endeditor endproc procedure addpitchpoint mid mani$ s1b s1e relt f0val # add one pitch point, in manipulation object passed as parameters, # at relative position relt within interval s1b to s1e, of value f0val select 'mid' ppt1 = s1b + (s1e-s1b)*relt # no need to open new ManipulationEditor window, use existing one # Edit editor Manipulation 'mani$' Add pitch point at... 'ppt1' 'f0val' endeditor endproc # after all duration points are added, we can close the Manipulation editor editor Manipulation 'mani$' Close endeditor # and rename the Manipulation object select 'manip1id' Rename... 'sent1$'_manip # and save the modified Manipulation object for later reference Write to binary file... 'indir$''sent1$'_manip.Manipulation # and resynthesize the original Sound with adjusted durations! Get resynthesis (overlap-add) printline Sound 'sent1$' modified; new sentence duration is 'cumdur:3' s # Als je wilt dat het gemanipuleerde geluidsbestand automatisch worden # weggeschreven, dan moet je het hekje en de daaropvolgende spatie # verwijderen uit de twee hiernavolgende regels: # Write to WAV file... 'indir$''sent1$'_manip.wav # printline saved as 'indir$''sent1$'_manip.wav # cleanup if replace_pitch = 1 # select and remove intermediate pitch objects select 'pitch1id' plus 'pitch2id' plus 'pitch1tempid' plus 'pitch2tempid' plus 'pitchtier1id' plus 'pitchtier2id' Remove endif # restore initial selection when this script was called? no # finish