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