001/**
002 * Portions Copyright 2001 Sun Microsystems, Inc.
003 * Portions Copyright 1999-2001 Language Technologies Institute, 
004 * Carnegie Mellon University.
005 * All Rights Reserved.  Use is subject to license terms.
006 * 
007 * See the file "license.terms" for information on usage and
008 * redistribution of this file, and for a DISCLAIMER OF ALL 
009 * WARRANTIES.
010 */
011package com.sun.speech.freetts.diphone;
012
013import java.io.IOException;
014import java.net.URL;
015
016import com.sun.speech.freetts.FeatureSet;
017import com.sun.speech.freetts.Item;
018import com.sun.speech.freetts.ProcessException;
019import com.sun.speech.freetts.Relation;
020import com.sun.speech.freetts.Utterance;
021import com.sun.speech.freetts.UtteranceProcessor;
022import com.sun.speech.freetts.relp.Sample;
023import com.sun.speech.freetts.relp.SampleInfo;
024
025
026/**
027 * Generates the Unit Relation of an Utterance from the
028 * Segment Relation.
029 */
030public class DiphoneUnitSelector implements UtteranceProcessor {
031
032    // the UnitDatabase to use
033    private DiphoneUnitDatabase diphoneDatabase;
034        
035    // The Utterance that this DiphoneUnitSelector works on
036    // private Utterance utterance;
037    
038    
039    /**
040     * Constructs a DiphoneUnitSelector.
041     *
042     * @param url the URL for the unit database. If the URL path ends
043     *     with a '.bin' it is assumed that the DB is a binary database,
044     *     otherwise, its assumed that its a text database1
045     *
046     * @throws IOException if an error occurs while loading the
047     *     database
048     *
049     */
050    public DiphoneUnitSelector(URL url) throws IOException {
051        if (url == null) {
052            throw new IOException("Can't load unit database");
053        }
054        boolean binary = url.getPath().endsWith(".bin");
055        diphoneDatabase = new DiphoneUnitDatabase(url, binary);
056    }
057    
058    /**
059     * Get the sample info for the underlying database.
060     * @return the sample info object
061     */
062    public SampleInfo getSampleInfo() {
063        return diphoneDatabase.getSampleInfo();
064    }
065    
066    /**
067     * Generates the Unit Relation from the Segment Relation.
068     *
069     * @param  utterance  the utterance to generate the Unit Relation
070     *
071     * @throws ProcessException if an IOException is thrown during the
072     *         processing of the utterance
073     */
074    public void processUtterance(Utterance utterance) throws ProcessException {
075
076        if (utterance.getRelation(Relation.SEGMENT) == null) {
077            throw new IllegalStateException
078                ("DiphoneUnitSelector: Segment relation does not exist");
079        }
080        
081        utterance.setObject(SampleInfo.UTT_NAME,
082                diphoneDatabase.getSampleInfo());
083        createUnitRelation(utterance);
084    }
085
086    /**
087     * Creates the Unit Relation in the given utterance from
088     * the diphone units and their some associated information from
089     * the units database.
090     *
091     * @param utterance the utterance that gets the new unit relation
092     */
093    private void createUnitRelation(Utterance utterance) {
094
095        Item segmentItem0, segmentItem1;
096        float end0, end1;
097        int targetEnd;
098        
099        Item unitItem0, unitItem1;
100                
101        String diphoneName;
102                        
103        Relation unitRelation = utterance.createRelation(Relation.UNIT);
104        Relation segmentRelation = utterance.getRelation(Relation.SEGMENT);
105
106        for (segmentItem0 = segmentRelation.getHead();
107                segmentItem0 != null && segmentItem0.getNext() != null;
108                segmentItem0 = segmentItem1) {
109            segmentItem1 = segmentItem0.getNext();
110            diphoneName = segmentItem0.getFeatures().getString("name") + "-" +
111                segmentItem1.getFeatures().getString("name");
112            
113
114            // First half of diphone
115            end0 = segmentItem0.getFeatures().getFloat("end");
116            targetEnd = (int) (end0 * 
117                    diphoneDatabase.getSampleInfo().getSampleRate());
118            unitItem0 = createUnitItem(unitRelation, diphoneName, targetEnd, 1);
119            segmentItem0.addDaughter(unitItem0);
120            
121            // Second half of diphone
122            end1 = segmentItem1.getFeatures().getFloat("end");
123            targetEnd = (int) (((end0 + end1)/2.0) * 
124                        diphoneDatabase.getSampleInfo().getSampleRate());
125            unitItem1 = createUnitItem(unitRelation, diphoneName, targetEnd, 2);
126            segmentItem1.addDaughter(unitItem1);
127        }
128    }
129
130    /**
131     * Returns a new Item (a Unit) in the given Relation, and
132     * sets the new Item to the given diphone name, target end,
133     * unit entry (index in the database), and unit part (1 or 2).
134     *
135     * @param unitRelation the relation that gets the new item
136     * @param diphoneName the name of the dipohone
137     * @param targetEnd the time at the end of this unit
138     * @param unitPart the item can be in the first(1) or second part (2)
139     */
140    private Item createUnitItem(Relation unitRelation,
141                                String diphoneName,
142                                int targetEnd,
143                                int unitPart) {
144        Diphone diphone = (Diphone) diphoneDatabase.getUnit(diphoneName);
145        if (diphone == null) {
146            System.err.println
147                ("FreeTTS: unit database failed to find entry for: " + 
148                 diphoneName);
149        }
150        Item unit = unitRelation.appendItem();
151        FeatureSet unitFeatureSet = unit.getFeatures();
152
153        unitFeatureSet.setString("name", diphoneName);
154        unitFeatureSet.setInt("target_end", targetEnd);
155        unitFeatureSet.setObject("unit", new DiphoneUnit(diphone, unitPart));
156        // unitFeatureSet.setInt("unit_part", unitPart);
157        return unit;
158    }
159
160    /**
161     * Returns a string representation of this object.
162     *
163     * @return a string representation of this object
164     */
165    public String toString() {
166        return "DiphoneUnitSelector";
167    }
168}
169
170
171/**
172 * A wrapper around the Diphone class that turns the
173 * diphone into a unit
174 */
175class DiphoneUnit implements com.sun.speech.freetts.Unit {
176
177    private Diphone diphone;
178    private int unitPart;
179
180    /**
181     * Contructs a diphone unit given a diphone and unit part.
182     *
183     * @param diphone the diphone to wrap
184     * @param unitPart which half (1 or 2) does this unit represent
185     */
186    public DiphoneUnit(Diphone diphone, int unitPart) {
187        this.diphone = diphone;
188        this.unitPart = unitPart;
189    }
190
191    /**
192     * Returns the name of this Unit.
193     *
194     * @return the name of the unit
195     */
196    public String getName() {
197        return diphone.getName();
198    }
199
200    /**
201     * Returns the size of the unit.
202     *
203     * @return the size of the unit
204     */
205    public int getSize() {
206        return diphone.getUnitSize(unitPart);
207    }
208
209    /**
210     * Retrieves the nearest sample.
211     *
212     * @param index the ideal index
213     *
214     * @return the nearest Sample
215     */
216    public Sample getNearestSample(float index) {
217        return diphone.nearestSample(index, unitPart); 
218    }
219
220    /**
221     * Returns a string representation of this object.
222     *
223     * @return a string representation of this object
224     */
225    public String toString() {
226        return getName();
227    }
228
229
230    /** 
231     * Dumps this unit.
232     */
233    public void dump()  {
234        diphone.dump();
235    }
236}