/**
* 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.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
*/
package com.sun.speech.freetts.diphone;
import com.sun.speech.freetts.FeatureSet;
import com.sun.speech.freetts.Item;
import com.sun.speech.freetts.relp.LPCResult;
import com.sun.speech.freetts.UtteranceProcessor;
import com.sun.speech.freetts.Utterance;
import com.sun.speech.freetts.Relation;
import com.sun.speech.freetts.ProcessException;
import com.sun.speech.freetts.relp.SampleInfo;
/**
* Calculates pitchmarks. This is an utterance processor that expects
* the utterance to have a target relation. It will create an
* LPCResult and add it to the utterance based upon features of the
* target relation.
*
* @see LPCResult
* @see Relation
* @see SampleInfo
*/
public class DiphonePitchmarkGenerator implements UtteranceProcessor {
/**
* Generates the LPCResult for this utterance.
*
* @param utterance the utterance to process
*
* @throws ProcessException if an error occurs while processing
* the utterance
* @throws IllegalStateException if the given utterance has no
* relation named Relation.TARGET or a feature named
* SampleInfo.UTT_NAME
*/
public void processUtterance(Utterance utterance) throws ProcessException {
// precondition that must be satisfied
Relation targetRelation = utterance.getRelation(Relation.TARGET);
if (targetRelation == null) {
throw new IllegalStateException
("DiphonePitchmarkGenerator: Target relation does not exist");
}
SampleInfo sampleInfo;
sampleInfo = (SampleInfo) utterance.getObject(SampleInfo.UTT_NAME);
if (sampleInfo == null) {
throw new IllegalStateException
("DiphonePitchmarkGenerator: SampleInfo does not exist");
}
float pos, f0, m = 0;
float lf0 = utterance.getVoice().getPitch();
double time = 0;
int pitchMarks = 0; // how many pitch marks
LPCResult lpcResult;
IntLinkedList timesList = new IntLinkedList();
// first pass to count how many pitch marks will be required
for (Item targetItem = targetRelation.getHead();
targetItem != null; targetItem = targetItem.getNext()) {
FeatureSet featureSet = targetItem.getFeatures();
pos = featureSet.getFloat("pos");
f0 = featureSet.getFloat("f0");
//System.err.println("Target pos="+pos+", f0="+f0);
if (time == pos) {
lf0 = f0;
continue;
}
m = (f0-lf0)/pos;
//System.err.println("m=("+f0+"-"+lf0+")/"+pos+"="+m);
for (; time < pos; pitchMarks++) {
time += 1/(lf0 + (time * m));
//System.err.println("f("+time+")="+((lf0+(time*m))));
// save the time value in a list
timesList.add((int) (time * sampleInfo.getSampleRate()));
}
lf0 = f0;
}
lpcResult = new LPCResult();
// resize the number of frames to the number of pitchmarks
lpcResult.resizeFrames(pitchMarks);
pitchMarks = 0;
int[] targetTimes = lpcResult.getTimes();
// second pass puts the values in
timesList.resetIterator();
for (; pitchMarks < targetTimes.length; pitchMarks++) {
targetTimes[pitchMarks] = timesList.nextInt();
}
utterance.setObject("target_lpcres", lpcResult);
}
/**
* Returns a string representation of this object.
*
* @return a string representation of this object
*/
public String toString() {
return "DiphonePitchmarkGenerator";
}
}
/**
* Represents a linked list with each node of the list storing
* a primitive int data type. Unlike the java.util.LinkedList, it avoids
* the need to wrap the float number in a Float object. This avoids
* unnecessary object creation, and is therefore faster and saves memory.
* However, it does not implement the java.util.List interface.
*
* This linked list is used as a replacement for a simple array of
* ints. Certain performance critical loops have had performance
* issues due to the overhead associated with array index bounds
* checking performed by the VM. Using this type of data structure
* allowed the checking to be bypassed. Note however that we've seen
* great improvement in compiler performance in this area such that we
* may be able to revert to using an array without any performance
* impact.
*
* [[[ TODO look at replacing this with a simple int array ]]]
*/
class IntLinkedList {
private IntListNode head = null;
private IntListNode tail = null;
private IntListNode iterator = null;
/**
* Constructs an empty IntLinkedList.
*/
public IntLinkedList() {
head = null;
tail = null;
iterator = null;
}
/**
* Adds the given float to the end of the list.
*
* @param val the float to add
*/
public void add(int val) {
IntListNode node = new IntListNode(val);
if (head == null) {
head = node;
tail = node;
} else {
tail.next = node;
tail = node;
}
}
/**
* Moves the iterator to point to the front of the list.
*/
public void resetIterator() {
iterator = head;
}
/**
* Returns the next float in the list, advances the iterator.
* The <code>hasNext()</code> method MUST be called before calling
* this method to check if the iterator is point to null,
* otherwise NullPointerException will be thrown.
*
* @return the next value
*/
public int nextInt() {
int val = iterator.val;
if (iterator != null) {
iterator = iterator.next;
}
return val;
}
/**
* Checks if there are more elements for the iterator.
*
* @return <code>true</code> if there are more elements;
* otherwise <code>false</code>
*/
public boolean hasNext() {
return (iterator != null);
}
}
/**
* Represents a node for the IntList
*/
class IntListNode {
int val;
IntListNode next;
/**
* Creates a node that wraps the given value.
*
* @param val the value to be contained in the list
*/
public IntListNode(int val) {
this.val = val;
next = null;
}
}