/** * Copyright 2007 DFKI GmbH. * All Rights Reserved. Use is subject to license terms. * * This file is part of MARY TTS. * * MARY TTS is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package marytts.tools.voiceimport; import java.io.File; import java.io.IOException; import java.util.SortedMap; import java.util.TreeMap; import javax.sound.sampled.UnsupportedAudioFileException; import marytts.signalproc.analysis.F0TrackerAutocorrelationHeuristic; import marytts.signalproc.analysis.PitchFileHeader; import marytts.signalproc.analysis.PitchMarks; import marytts.signalproc.analysis.PitchReaderWriter; import marytts.util.data.ESTTrackWriter; import marytts.util.signal.SignalProcUtils; /** * A pitch marker component that uses F0TrackerAutocorrelationHeuristic. * * @author Oytun Türk * */ public class AutocorrelationPitchmarker extends VoiceImportComponent { protected DatabaseLayout db = null; protected String ptcExt = ".ptc"; private int percent = 0; public final String WINSIZE = "AutocorrelationPitchmarker.windowSizeInSeconds"; // Window size in seconds public final String SKIPSIZE = "AutocorrelationPitchmarker.skipSizeInSeconds"; // Skip size in seconds public final String VOICINGTH = "AutocorrelationPitchmarker.voicingThreshold"; // Voicing threshold public final String MINF0 = "AutocorrelationPitchmarker.minimumF0"; // Min f0 in Hz public final String MAXF0 = "AutocorrelationPitchmarker.maximumF0"; // Max f0 in Hz public final String PTCDIR = "AutocorrelationPitchmarker.ptcDir"; public final String PMDIR = "db.pmDir"; public final String PMEXT = "db.pmExtension"; protected void setupHelp() { if (props2Help == null) { props2Help = new TreeMap(); PitchFileHeader tmp = new PitchFileHeader(); props2Help.put(WINSIZE, "window size in pitch detection. Default: " + String.valueOf(tmp.windowSizeInSeconds)); props2Help.put(SKIPSIZE, "skip size in pitch detection. Default: " + String.valueOf(tmp.skipSizeInSeconds)); props2Help .put(VOICINGTH, "threshold of voicing (decrease to get more voiced frames). Default: " + String.valueOf(tmp.voicingThreshold)); props2Help.put(MINF0, "minimum value for the pitch (in Hz). Default: " + String.valueOf(tmp.minimumF0)); props2Help.put(MAXF0, "maximum value for the pitch (in Hz). Default: " + String.valueOf(tmp.maximumF0)); props2Help.put(PTCDIR, "directory containing the binary f0 contour files. Will be created if" + "it does not exist"); } } public final String getName() { return "AutocorrelationPitchmarker"; } public SortedMap getDefaultProps(DatabaseLayout db) { this.db = db; if (props == null) { props = new TreeMap(); PitchFileHeader tmp = new PitchFileHeader(); props.put(WINSIZE, String.valueOf(tmp.windowSizeInSeconds)); props.put(SKIPSIZE, String.valueOf(tmp.skipSizeInSeconds)); props.put(VOICINGTH, String.valueOf(tmp.voicingThreshold)); props.put(MINF0, String.valueOf(tmp.minimumF0)); props.put(MAXF0, String.valueOf(tmp.maximumF0)); String rootDir = db.getProp(db.ROOTDIR); props.put(PTCDIR, rootDir + "ptc" + System.getProperty("file.separator")); } return props; } private boolean extractPitchmarks(String basename, PitchFileHeader params) throws IOException { String wavFilename = new File(db.getProp(db.WAVDIR) + basename + db.getProp(db.WAVEXT)).getAbsolutePath(); String ptcFile = getProp(PTCDIR) + basename + ptcExt; String pmFilename = db.getProp(PMDIR) + basename + db.getProp(PMEXT); WavReader wav = new WavReader(wavFilename); int fs = wav.getSampleRate(); short[] x = wav.getSamples(); F0TrackerAutocorrelationHeuristic pitchDetector = new F0TrackerAutocorrelationHeuristic(params); PitchReaderWriter f0 = null; try { f0 = pitchDetector.pitchAnalyzeWavFile(wavFilename, ptcFile); } catch (UnsupportedAudioFileException e) { System.out.println("Error! Cannot perform pitch detection..."); } if (f0 != null) { PitchMarks pm = SignalProcUtils.pitchContour2pitchMarks(f0.contour, fs, x.length, f0.header.windowSizeInSeconds, f0.header.skipSizeInSeconds, false, 0); // Now convert to EST pm format float[] pitchmarks = new float[pm.pitchMarks.length]; for (int i = 0; i < pitchmarks.length; i++) pitchmarks[i] = SignalProcUtils.sample2time(pm.pitchMarks[i], fs); new ESTTrackWriter(pitchmarks, null, "pitchmarks").doWriteAndClose(pmFilename, false, false); } return true; } /** * The standard compute() method of the VoiceImportComponent interface. * * @throws IOException * IOException */ public boolean compute() throws IOException { String[] baseNameArray = bnl.getListAsArray(); System.out.println("Computing pitchmarks for " + baseNameArray.length + " utterances."); /* Ensure the existence of the target directory for corrected pitchmarks */ File dir = new File(db.getProp(PMDIR)); if (!dir.exists()) { System.out.println("Creating the directory [" + db.getProp(PMDIR) + "]."); dir.mkdir(); } dir = new File(getProp(PTCDIR)); if (!dir.exists()) { System.out.println("Creating the directory [" + getProp(PTCDIR) + "]."); dir.mkdir(); } PitchFileHeader params = new PitchFileHeader(); params.windowSizeInSeconds = Double.valueOf(getProp(WINSIZE)); params.skipSizeInSeconds = Double.valueOf(getProp(SKIPSIZE)); params.voicingThreshold = Double.valueOf(getProp(VOICINGTH)); params.minimumF0 = Double.valueOf(getProp(MINF0)); params.maximumF0 = Double.valueOf(getProp(MAXF0)); System.out.println("Running autocorrelation based pitch marker..."); for (int i = 0; i < baseNameArray.length; i++) { percent = 100 * i / baseNameArray.length; extractPitchmarks(baseNameArray[i], params); } System.out.println("Autocorrelation based pitch marking completed."); return true; } /** * Provide the progress of computation, in percent, or -1 if that feature is not implemented. * * @return -1 if not implemented, or an integer between 0 and 100. */ public int getProgress() { return percent; } public static void main(String[] args) throws Exception { VoiceImportComponent vic = new AutocorrelationPitchmarker(); DatabaseLayout db = new DatabaseLayout(vic); vic.compute(); } }