001/**
002 * Copyright 2002 DFKI GmbH.
003 * All Rights Reserved.  Use is subject to license terms.
004 *
005 * See the file "license.terms" for information on usage and
006 * redistribution of this file, and for a DISCLAIMER OF ALL
007 * WARRANTIES.
008 */
009package de.dfki.lt.freetts.mbrola;
010
011import java.io.IOException;
012import java.nio.ByteOrder;
013import java.util.Iterator;
014import java.util.List;
015import java.util.logging.Level;
016import java.util.logging.Logger;
017
018import javax.sound.sampled.AudioFormat;
019
020import com.sun.speech.freetts.ProcessException;
021import com.sun.speech.freetts.Utterance;
022import com.sun.speech.freetts.UtteranceProcessor;
023import com.sun.speech.freetts.audio.AudioPlayer;
024
025/**
026 * Supports generating audio output from an MBROLA-synthesized utterance. This
027 * is an utterance processor. The primary method, <code> processUtterance
028 * </code> takes an utterance containing an open BufferedInputStream, from
029 * which to read raw audio data provided by the external MBROLA binary. The
030 * audio data is read and sent to the proper audio player.
031 *
032 */
033public class MbrolaAudioOutput implements UtteranceProcessor {
034    /** Logger instance. */
035    private static final Logger LOGGER =
036        Logger.getLogger(MbrolaAudioOutput.class.getName());
037
038    /**
039     * The raw audio data coming out of MBROLA is in native byte order,
040     * 16 kHz, 16 bit, mono
041     */
042    private final static AudioFormat MBROLA_AUDIO =
043            new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
044                            16000, // samples per second
045                            16, // bits per sample
046                            1, // mono
047                            2, // nr. of bytes per frame
048                            16000, // nr. of frames per second
049                            ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN);
050    
051    /**
052     * Reads audio data generated by the external MBROLA binary for the given
053     * Utterance. The data is read from the open BufferedInputStream associated
054     * with the Utterance, and written into the AudioPlayer.
055     *
056     * @param  utterance  the utterance to generate waves
057     *
058     * @throws ProcessException if an IOException is thrown during the
059     *         processing of the utterance
060     */
061    public void processUtterance(Utterance utterance) throws ProcessException {
062        if (LOGGER.isLoggable(Level.FINE)) {
063            LOGGER.fine("=== " +
064                                 utterance.getString("input_text"));
065        }
066
067        AudioPlayer audioPlayer = utterance.getVoice().getAudioPlayer();
068
069        audioPlayer.setAudioFormat(MBROLA_AUDIO);
070        audioPlayer.setVolume(utterance.getVoice().getVolume());
071
072        // The AudioPlayer interface currently does not allow streaming audio.
073        // We need to know the total number of samples that will be written
074        // before we can start writing them. Therefore, we need to load all
075        // audio data for this utterance into RAM.
076        
077        List audioData = (List) utterance.getObject("mbrolaAudio");
078        if (audioData == null) {
079            throw new ProcessException
080                ("No \"mbrolaAudio\" object is associated with utterance");
081        }
082
083        // total number of audio bytes
084
085        int totalSize;
086        try {
087            totalSize = utterance.getInt("mbrolaAudioLength");
088        } catch (NullPointerException npe) {
089            totalSize = 0;
090        }
091
092        try {
093            audioPlayer.begin(totalSize);
094        } catch (IOException e) {
095            throw new ProcessException(e.getMessage(), e);
096        }
097
098        for (Iterator it = audioData.iterator(); it.hasNext();) {
099            byte[] bytes = (byte[]) it.next();
100            try {
101                if (!audioPlayer.write(bytes)) {
102                    throw new ProcessException
103                        ("Cannot write audio data to audio player");
104                }
105            } catch (IOException e) {
106                throw new ProcessException(e.getMessage(), e);
107            }
108        }
109
110        try {
111            if (!audioPlayer.end()) {
112                throw new ProcessException("audio player reports problem");
113            }
114        } catch (IOException e) {
115            throw new ProcessException(e.getMessage(), e);
116        }
117    }
118
119    
120    /**
121     * 
122     * Returns the string form of this object
123     * 
124     * @return the string form of this object
125     */
126    public String toString() {
127        return "MbrolaAudioOutput";
128    }
129}
130
131
132
133