All Downloads are FREE. Search and download functionalities are using the official Maven repository.

marytts.tools.voiceimport.TimelineWriter Maven / Gradle / Ivy

The newest version!
/**
 * Portions Copyright 2006 DFKI GmbH.
 * Portions Copyright 2001 Sun Microsystems, Inc.
 * Portions Copyright 1999-2001 Language Technologies Institute, 
 * Carnegie Mellon University.
 * All Rights Reserved.  Use is subject to license terms.
 * 
 * Permission is hereby granted, free of charge, to use and distribute
 * this software and its documentation without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of this work, and to
 * permit persons to whom this work is furnished to do so, subject to
 * the following conditions:
 * 
 * 1. The code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 * 2. Any modifications must be clearly marked as such.
 * 3. Original authors' names are not deleted.
 * 4. The authors' names are not used to endorse or promote products
 *    derived from this software without specific prior written
 *    permission.
 *
 * DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE
 * CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 * THIS SOFTWARE.
 */
package marytts.tools.voiceimport;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Vector;

import marytts.unitselection.data.TimelineReader;
import marytts.util.data.Datagram;
import marytts.util.data.MaryHeader;

/**
 * The TimelineWriter class provides an interface to create or update a Timeline data file in Mary format, and to feed new
 * datagrams to the timeline file.
 * 
 * @author sacha, marc
 *
 */
public class TimelineWriter {

	protected RandomAccessFile raf = null; // The file to read from
	protected MaryHeader maryHdr = null; // The standard Mary header
	protected TimelineReader.ProcHeader procHdr = null; // The processing info header

	protected TimelineReader.Index idx = null; // A global time index for the variable-sized datagrams

	/* Some specific header fields: */
	protected int sampleRate = 0;
	protected long numDatagrams = 0;

	protected long datagramsBytePos = 0;
	protected long timeIdxBytePos = 0;

	/* Pointers to navigate the file: */
	protected long timePtr = 0; // A time pointer to keep track of the time position in the file
	// Note: a file pointer, keeping track of the byte position in the file, is implicitely
	// maintained by the browsed RandomAccessFile.

	/****************/
	/* DATA FIELDS */
	/****************/
	private int idxInterval;
	private long datagramZoneBytePos;
	private Vector indexData;
	private long prevBytePos;
	private long prevTimePos;

	/****************/
	/* CONSTRUCTORS */
	/****************/

	/**
	 * Constructor to create a timeline.
	 * 
	 * @param fileName
	 *            The file to read the timeline from.
	 * @param procHdrString
	 *            the string to use as a processing header.
	 * @param reqSampleRate
	 *            the sample rate requested to measure time in this timeline.
	 * @param setIdxIntervalInSeconds
	 *            the interval between two index entries, in seconds
	 */
	public TimelineWriter(String fileName, String procHdrString, int reqSampleRate, double setIdxIntervalInSeconds) {

		/* Check the arguments */
		if (reqSampleRate <= 0) {
			throw new RuntimeException("The sample rate [" + reqSampleRate
					+ "] can't be negative or null when creating a timeline.");
		}
		if (setIdxIntervalInSeconds <= 0.0) {
			throw new RuntimeException("The index interval [" + setIdxIntervalInSeconds
					+ "] can't be negative or null when creating a timeline.");
		}

		/* Open the file */
		try {
			File fid = new File(fileName);
			/* Check if the file exists and should be deleted first. */
			if (fid.exists())
				fid.delete();
			/* open */
			raf = new RandomAccessFile(fid, "rw");
		} catch (FileNotFoundException e) {
			throw new Error("Timeline file [" + fileName + "] was not found.");
		} catch (SecurityException e) {
			throw new Error("You do not have read access to the file [" + fileName + "].");
		}

		/* Make a new header */
		try {
			/* Make a new Mary header and write it */
			maryHdr = new MaryHeader(MaryHeader.TIMELINE);
			maryHdr.writeTo(raf);

			/* Make a new processing header and write it */
			procHdr = new TimelineReader.ProcHeader(procHdrString);
			procHdr.dump(raf);

			/* Make/write the data header */
			sampleRate = reqSampleRate;
			raf.writeInt(sampleRate);

			numDatagrams = 0;
			raf.writeLong(numDatagrams);

			/* Write the positions, with fake ones for the idx and basenames */
			datagramsBytePos = getBytePointer() + 16; // +16: account for the 2 upcoming long fields datagramsBytePos and
														// timeIdxBytePos
			raf.writeLong(datagramsBytePos);
			timeIdxBytePos = 0;
			raf.writeLong(0l);

			// Remember important facts for index creation
			idxInterval = (int) Math.round(setIdxIntervalInSeconds * (double) sampleRate);
			datagramZoneBytePos = datagramsBytePos;
			indexData = new Vector();
			prevBytePos = datagramsBytePos;
			prevTimePos = 0;

			/* Now we can output the datagrams. */

		} catch (IOException e) {
			throw new RuntimeException("IOException caught when constructing a timeline writer on file [" + fileName + "]: ", e);
		}
	}

	/*******************/
	/* MISC. METHODS */
	/*******************/

	/**
	 * Get the current byte position in the file
	 * 
	 * @throws IOException
	 *             IOException
	 * @return raf.getFilePointer
	 */
	public synchronized long getBytePointer() throws IOException {
		return (raf.getFilePointer());
	}

	/**
	 * Get the current time position in the file
	 * 
	 * @return timePtr
	 */
	public synchronized long getTimePointer() {
		return (timePtr);
	}

	/**
	 * Set the current byte position in the file
	 * 
	 * @param bytePos
	 *            bytePos
	 * @throws IOException
	 *             IOException
	 */
	protected void setBytePointer(long bytePos) throws IOException {
		raf.seek(bytePos);
	}

	/**
	 * Set the current time position in the file
	 * 
	 * @param timePosition
	 *            timePosition
	 */
	protected void setTimePointer(long timePosition) {
		timePtr = timePosition;
	}

	/**
	 * Scales a discrete time to the timeline's sample rate.
	 * 
	 * @param reqSampleRate
	 *            the externally given sample rate.
	 * @param targetTimeInSamples
	 *            a discrete time, with respect to the externally given sample rate.
	 * 
	 * @return a discrete time, in samples with respect to the timeline's sample rate.
	 */
	protected long scaleTime(int reqSampleRate, long targetTimeInSamples) {
		if (reqSampleRate == sampleRate)
			return (targetTimeInSamples);
		/* else */return ((long) Math.round((double) (reqSampleRate) * (double) (targetTimeInSamples) / (double) (sampleRate)));
	}

	/**
	 * Unscales a discrete time from the timeline's sample rate.
	 * 
	 * @param reqSampleRate
	 *            the externally given sample rate.
	 * @param timelineTimeInSamples
	 *            a discrete time, with respect to the timeline sample rate.
	 * 
	 * @return a discrete time, in samples with respect to the externally given sample rate.
	 */
	protected long unScaleTime(int reqSampleRate, long timelineTimeInSamples) {
		if (reqSampleRate == sampleRate)
			return (timelineTimeInSamples);
		/* else */return ((long) Math.round((double) (sampleRate) * (double) (timelineTimeInSamples) / (double) (reqSampleRate)));
	}

	public TimelineReader.Index getIndex() {
		return idx;
	}

	/**
	 * Returns the position of the datagram zone
	 * 
	 * @return datagramsBytePos
	 */
	public long getDatagramsBytePos() {
		return datagramsBytePos;
	}

	/**
	 * Returns the current number of datagrams in the timeline.
	 * 
	 * @return numDatagrams
	 */
	public long getNumDatagrams() {
		return numDatagrams;
	}

	/**
	 * Returns the sample rate of the timeline.
	 * 
	 * @return sampleRate
	 */
	public int getSampleRate() {
		return sampleRate;
	}

	/**
	 * Output the internally maintained indexes and close the file.
	 * 
	 * @throws IOException
	 *             IOException
	 */
	public void close() throws IOException {

		/* Correct the number of datagrams */
		setBytePointer(datagramsBytePos - 24l);
		raf.writeLong(numDatagrams);

		/* Go to the end of the file and output the time index */
		timeIdxBytePos = raf.length();
		setBytePointer(timeIdxBytePos);
		idx = new TimelineReader.Index(idxInterval, indexData);
		idx.dump(raf);

		/* Register the index positions */
		setBytePointer(datagramsBytePos - 8l);
		raf.writeLong(timeIdxBytePos);

		/* Finally, close the random access file */
		raf.close();
	}

	/**
	 * Feeds a file position (in bytes) and a time position (in samples) from a timeline, and determines if a new index field is
	 * to be added.
	 * 
	 * @param bytePosition
	 *            bytePosition
	 * @param timePosition
	 *            timePosition
	 * @return the number of index fields after the feed.
	 */
	private void feedIndex(long bytePosition, long timePosition) {
		/* Get the time associated with the yet to come index field */
		long nextIdxTime = indexData.size() * (long) idxInterval;
		/*
		 * If the current time position passes the next possible index field, register the PREVIOUS datagram position in the new
		 * index field
		 */
		while (nextIdxTime < timePosition) {
			// System.out.println( "Hitting a new index at position\t[" + bytePosition + "," + timePosition + "]." );
			// System.out.println( "The crossed index is [" + nextIdxTime + "]." );
			// System.out.println( "The registered (previous) position is\t[" + prevBytePos + "," + prevTimePos + "]." );
			// IdxField testField = (IdxField)field.elementAt(currentNumIdx-1);
			// System.out.println( "The previously indexed position was\t[" + testField.bytePtr + "," + testField.timePtr + "]."
			// );

			indexData.add(new TimelineReader.IdxField(prevBytePos, prevTimePos));
			nextIdxTime += idxInterval;
		}

		/* Memorize the observed datagram position */
		prevBytePos = bytePosition;
		prevTimePos = timePosition;
	}

	/**
	 * Write one datagram to the timeline.
	 * 
	 * @param d
	 *            the datagram to write.
	 * @param reqSampleRate
	 *            the sample rate at which the datagram duration is expressed.
	 * @throws IOException
	 *             IOException
	 */
	public void feed(Datagram d, int reqSampleRate) throws IOException {
		// System.out.println( "Feeding datagram [ " + d.data.length + " , " + d.duration + " ] at pos ( "
		// + getBytePointer() + " , " + getTimePointer() + " )" );
		/* Filter the datagram through the index (to automatically add an index field if needed) */
		feedIndex(getBytePointer(), getTimePointer());
		/* Check if the datagram needs resampling */
		if (reqSampleRate != sampleRate)
			d.setDuration(scaleTime(reqSampleRate, d.getDuration()));
		/* Then write the datagram on disk */
		d.write(raf); // This implicitely advances the bytePointer
		/* Then advance various other pointers */
		setTimePointer(getTimePointer() + d.getDuration());
		numDatagrams++;
		// System.out.println( "Reached pos ( " + getBytePointer() + " , " + getTimePointer() + " )" );

	}

	/**
	 * Write a series of datagrams to the timeline.
	 * 
	 * @param dArray
	 *            an array of datagrams.
	 * @param reqSampleTime
	 *            the sample rate at which the datagram durations are expressed.
	 * @throws IOException
	 *             IOException
	 */
	public void feed(Datagram[] dArray, int reqSampleTime) throws IOException {
		for (int i = 0; i < dArray.length; i++) {
			feed(dArray[i], reqSampleTime);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy