def resynthesizePitch(praatEXE, inputWavFN, pitchFN, outputWavFN, minPitch, maxPitch, scriptFN=None, pointList=None): ''' Resynthesizes the pitch in a wav file with the given pitch contour file The pitch track to use can optionally be passed in as pointList. If so, it will be saved as pitchFN for praat to be able to use. ''' if scriptFN is None: scriptFN = join(utils.scriptsPath, "resynthesize_pitch.praat") if pointList is not None: dur = audioio.WavQueryObj(inputWavFN).getDuration() pointObj = dataio.PointObject2D(pointList, dataio.PITCH, 0, dur) pointObj.save(pitchFN) utils.runPraatScript( praatEXE, scriptFN, [inputWavFN, pitchFN, outputWavFN, minPitch, maxPitch])
def segment_audio(audiodir, wavdir, tgs, long2iso): print('Segmenting %s from %s to %s'%(str([x for x in tgs.keys()]),wavdir,audiodir)) outputnames = [] for (filename,tg) in tgs.items(): entryList = tg.tierDict['seg'].entryList entryList = [ entry for entry in entryList if not entry[2].isspace() ] outdir = os.path.join(audiodir,filename) os.makedirs(outdir,exist_ok=True) inputfile = os.path.join(wavdir, filename+'.wav') wavQObj = audioio.WavQueryObj(inputfile) for entry in entryList: start, stop, label = entry if not label.isnumeric(): raise RunTimeError('TextGrid %s contains non-numeric label %s'%(filename,label)) outputname = os.path.join(outdir, '%s_%4.4d.wav'%(filename,int(label))) frames = wavQObj.getFrames(start, stop) wavQObj.outputModifiedWav(frames, outputname) outputnames.append(outputname)
def deleteVowels(inputTGFN, inputWavFN, outputPath, doShrink, atZeroCrossing=True): utils.makeDir(outputPath) wavFN = os.path.split(inputWavFN)[1] tgFN = os.path.split(inputTGFN)[1] outputWavFN = join(outputPath, wavFN) outputTGFN = join(outputPath, tgFN) if atZeroCrossing is True: zeroCrossingTGPath = join(outputPath, "zero_crossing_tgs") zeroCrossingTGFN = join(zeroCrossingTGPath, tgFN) utils.makeDir(zeroCrossingTGPath) tg = tgio.openTextgrid(inputTGFN) wavObj = audioio.WavQueryObj(inputWavFN) praatio_scripts.tgBoundariesToZeroCrossings(tg, wavObj, zeroCrossingTGFN) else: tg = tgio.openTextgrid(inputTGFN) keepList = tg.tierDict["phone"].entryList keepList = [entry for entry in keepList if not isVowel(entry[2])] deleteList = utils.invertIntervalList(keepList, tg.maxTimestamp) wavObj = audioio.openAudioFile(inputWavFN, keepList=keepList, doShrink=doShrink) wavObj.save(outputWavFN) shrunkTG = copy.deepcopy(tg) for start, stop in sorted(deleteList, reverse=True): shrunkTG = shrunkTG.eraseRegion(start, stop, doShrink=doShrink) shrunkTG.save(outputTGFN)
from praatio import tgio from praatio import audioio path = join(".", "files") outputPath = join(path, "anonymized_data") if not os.path.exists(outputPath): os.mkdir(outputPath) for wavFN, tgFN in (("mary.wav", "mary.TextGrid"), ("bobby.wav", "bobby_words.TextGrid")): outputWavFN = join(outputPath, wavFN) # Find the word(s) to anonymize # (One could imagine a search for common names or identification of # some sort of code ('section-to-anonymize') rather than what I have # done here. deleteList = [] tg = tgio.openTextgrid(join(path, tgFN)) deleteList.append(tg.tierDict['word'].entryList[0]) # Get only time information from entries (i.e. remove label information) deleteList = [(start, stop) for start, stop, _ in deleteList] # Replace segments with a sine wave wavQObj = audioio.WavQueryObj(join(path, wavFN)) wavQObj.deleteWavSections(outputWavFN, deleteList=deleteList, operation="sine wave")
originalPitchFN = "mary1.pitch" outputWavFN = "mary1_accented.wav" outputPitchFN = "mary1_accented.pitch" minPitch = 75 maxPitch = 450 if not os.path.exists(rootOutputPath): os.mkdir(rootOutputPath) # 1st - get pitch piList = pitch_and_intensity.extractPI(join(root, wavFN), join(rootOutputPath, pitchIntensityFN), praatEXE, minPitch, maxPitch) pitchList = [(timeV, pitchV) for timeV, pitchV, _ in piList] dur = audioio.WavQueryObj(join(root, wavFN)).getDuration() pointObj = dataio.PointObject2D(pitchList, dataio.PITCH, 0, dur) pointObj.save(join(rootOutputPath, originalPitchFN)) # 2nd - get region to manipulate. Let's make the subject more emphatic! tg = tgio.openTextgrid(join(root, "mary1.TextGrid")) tier = tg.tierDict["words"] start, stop, _ = tier.entryList[0] # Getting info for the first word targetPitchList = [(timeV, pitchV) for timeV, pitchV in pitchList if timeV >= start and timeV <= stop] # 3rd - make manipulation accent = modify_pitch_accent.PitchAccent(targetPitchList) accent.addPlateau(0.05) # Peak is dragged out for 0.05 seconds accent.adjustPeakHeight(60) # Plateau is raised by 60 hz
def splitAudioOnTier(wavFN, tgFN, tierName, outputPath, outputTGFlag=False, nameStyle=None, noPartialIntervals=False, silenceLabel=None): ''' Outputs one subwav for each entry in the tier of a textgrid outputTGFlag: If True, outputs paired, cropped textgrids If is type str (a tier name), outputs a paired, cropped textgrid with only the specified tier nameStyle: if 'append': append interval label to output name if 'append_no_i': append label but not interval to output name if 'label': output name is the same as label if None: output name plus the interval number noPartialIntervals: if True: intervals in non-target tiers that are not wholly contained by an interval in the target tier will not be included in the output textgrids silenceLabel: the label for silent regions. If silences are unlabeled intervals (i.e. blank) then leave this alone. If silences are labeled using praat's "annotate >> to silences" then this value should be "silences" ''' if not os.path.exists(outputPath): os.mkdir(outputPath) if noPartialIntervals is True: mode = 'strict' else: mode = 'truncated' tg = tgio.openTextgrid(tgFN) entryList = tg.tierDict[tierName].entryList if silenceLabel is not None: entryList = [entry for entry in entryList if entry[2] != silenceLabel] # Build the output name template name = os.path.splitext(os.path.split(wavFN)[1])[0] orderOfMagnitude = int(math.floor(math.log10(len(entryList)))) # We want one more zero in the output than the order of magnitude outputTemplate = "%s_%%0%dd" % (name, orderOfMagnitude + 1) firstWarning = True # If we're using the 'label' namestyle for outputs, all of the # interval labels have to be unique, or wave files with those # labels as names, will be overwritten if nameStyle == 'label': wordList = [word for _, _, word in entryList] multipleInstList = [] for word in set(wordList): if wordList.count(word) > 1: multipleInstList.append(word) if len(multipleInstList) > 0: instListTxt = "\n".join(multipleInstList) print(("Overwriting wave files in: %s\n" + "Intervals exist with the same name:\n%s") % (outputPath, instListTxt)) firstWarning = False # Output wave files outputFNList = [] wavQObj = audioio.WavQueryObj(wavFN) for i, entry in enumerate(entryList): start, stop, label = entry # Resolve output name outputName = outputTemplate % i if nameStyle == "append": outputName += "_" + label elif nameStyle == "append_no_i": outputName = name + "_" + label elif nameStyle == "label": outputName = label outputFNFullPath = join(outputPath, outputName + ".wav") if os.path.exists(outputFNFullPath) and firstWarning: print(("Overwriting wave files in: %s\n" + "Files existed before or intervals exist with " + "the same name:\n%s") % (outputPath, outputName)) frames = wavQObj.getFrames(start, stop) wavQObj.outputModifiedWav(frames, outputFNFullPath) outputFNList.append((start, stop, outputName + ".wav")) # Output the textgrid if requested if outputTGFlag is not False: subTG = tg.crop(start, stop, mode, True) if isinstance(outputTGFlag, str): for tierName in subTG.tierNameList: if tierName != outputTGFlag: subTG.removeTier(tierName) subTG.save(join(outputPath, outputName + ".TextGrid")) return outputFNList