/*
* FreeTTSClient.java (2001)
*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
*
* Unpublished - rights reserved under the Copyright Laws of the United States.
*
* Sun Microsystems, Inc. has intellectual property rights relating to
* technology embodied in the product that is described in this document. In
* particular, and without limitation, these intellectual property rights may
* include one or more of the U.S. patents listed at http://www.sun.com/patents
* and one or more additional patents or pending patent applications in the
* U.S. and in other countries.
*
* SUN PROPRIETARY/CONFIDENTIAL.
*
* U.S. Government Rights - Commercial software. Government users are subject
* to the Sun Microsystems, Inc. standard license agreement and applicable
* provisions of the FAR and its supplements.
*
* Use is subject to license terms.
*
* This distribution may include materials developed by third parties. Sun, Sun
* Microsystems, the Sun logo, Java, Jini, Solaris and Sun Ray are trademarks
* or registered trademarks of Sun Microsystems, Inc. in the U.S. and other
* countries.
*
* UNIX is a registered trademark in the U.S. and other countries, exclusively
* licensed through X/Open Company, Ltd.
*/
package com.sun.voip;
import com.sun.voip.Logger;
import com.sun.speech.freetts.Voice;
import com.sun.speech.freetts.VoiceManager;
import com.sun.speech.freetts.util.Utilities;
import com.sun.speech.freetts.audio.AudioPlayer;
import javax.sound.sampled.AudioFormat;
import java.io.IOException;
/**
* Implements a Java Client for the Client/Server demo. For details about
* the protocol between client and server, consult the file
* <code>Protocol.txt</code>.
*/
public class FreeTTSClient {
private static boolean initialized = false;
private static int sampleRate = Utilities.getInteger("sampleRate", 16000).intValue();
private static Voice voice;
private static String voice16kName = Utilities.getProperty("voice16kName", "kevin16");
private FreeTTSClient() {
}
public static void initialize() throws IOException {
if (initialized) {
return;
}
initialized = true;
VoiceManager voiceManager = VoiceManager.getInstance();
voice = voiceManager.getVoice(voice16kName);
voice.allocate();
}
/**
* Run the TTS protocol.
*/
public static int[] textToSpeech(String text) throws IOException {
long startTime = System.currentTimeMillis();
try {
ServerAudioPlayer serverAudioPlayer = new ServerAudioPlayer();
voice.setAudioPlayer(serverAudioPlayer);
voice.speak(text);
int[] linearData = serverAudioPlayer.getLinearData();
if (Logger.logLevel >= Logger.LOG_INFO) {
Logger.println("TTS time for '" + text + "' "
+ (System.currentTimeMillis() - startTime));
}
return linearData;
} catch (Exception ioe) {
ioe.printStackTrace();
}
return null;
}
}
/**
* Implements the AudioPlayer for the freetts Client/Server demo.
* This SocketAudioPlayer basically sends synthesized wave bytes to the
* client.
*/
class ServerAudioPlayer implements AudioPlayer {
private AudioFormat audioFormat;
private boolean debug = false;
private int bytesToPlay = 0;
private int bytesPlayed = 0;
private byte[] byteLinearData;
public ServerAudioPlayer() {
}
/**
* Sets the audio format to use for the next set of outputs. Since
* an audio player can be shared by a number of voices, and since
* voices can have different AudioFormats (sample rates for
* example), it is necessary to allow clients to dynamically set
* the audio format for the player.
*
* @param format the audio format
*/
public void setAudioFormat(AudioFormat format) {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("setAudioFormat");
}
this.audioFormat = format;
}
/**
* Retrieves the audio format for this player
*
* @return the current audio format
*
*/
public AudioFormat getAudioFormat() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("getAudioFormat");
}
return this.audioFormat;
}
/**
* Pauses all audio output on this player. Play can be resumed
* with a call to resume. Not implemented in this Player.
*/
public void pause() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("pause");
}
}
/**
* Resumes audio output on this player. Not implemented in this Player.
*/
public void resume() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("resume");
}
}
/**
* Prepares for another batch of output. Larger groups of output
* (such as all output associated with a single FreeTTSSpeakable)
* should be grouped between a reset/drain pair.
*/
public void reset() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("reset");
}
}
/**
* Flushes all the audio data to the Socket.
*
* @return <code>true</code> all the time
*/
public boolean drain() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("drain");
}
return true;
}
/**
* Starts the output of a set of data. Audio data for a single
* utterance should be grouped between begin/end pairs.
*
* @param size the size of data in bytes to be output before
* <code>end</code> is called.
*/
public void begin(int size) {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("begin size " + size);
}
}
/**
* Starts the first sample timer (none in this player)
*/
public void startFirstSampleTimer() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("startFirstSampleTimer");
}
}
/**
* Signals the end of a set of data. Audio data for a single
* utterance should be groupd between <code> begin/end </code> pairs.
*
* @return <code>true</code> if the audio was output properly,
* <code> false</code> if the output was cancelled
* or interrupted.
*
*/
public boolean end() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("end bytesPlayed " + bytesPlayed);
}
if (bytesPlayed < bytesToPlay) {
int bytesNotPlayed = bytesToPlay - bytesPlayed;
write(new byte[bytesNotPlayed], 0, bytesNotPlayed);
}
bytesToPlay = 0;
bytesPlayed = 0;
return true;
}
/**
* Cancels all queued output. All 'write' calls until the next
* reset will return false. Not implemented in this Player.
*
*/
public void cancel() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("cancel");
}
}
/**
* Waits for all audio playback to stop, and closes this AudioPlayer.
* Not implemented in this Player.
*/
public void close() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("close");
}
}
/**
* Returns the current volume. The volume is specified as a number
* between 0.0 and 1.0, where 1.0 is the maximum volume and 0.0 is
* the minimum volume. Not implemented in this Player.
*
* @return the current volume (between 0 and 1)
*/
public float getVolume() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("getVolume");
}
return -1;
}
/**
* Sets the current volume. The volume is specified as a number
* between 0.0 and 1.0, where 1.0 is the maximum volume and 0.0 is
* the minimum volume. Not implemented in this Player.
*
* @param volume the new volume (between 0 and 1)
*/
public void setVolume(float volume) {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("setVolume");
}
}
/**
* Gets the amount of audio played since the last resetTime.
* Not implemented in this Player.
*
* @returns the amount of audio in milliseconds
*/
public long getTime() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("getTime");
}
return -1;
}
/**
* Resets the audio clock. Not implemented in this Player.
*/
public void resetTime() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("reset");
}
}
/**
* Shows metrics for this audio player. Not implemented in this Player.
*/
public void showMetrics() {
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("showMetrics");
}
}
/**
* Writes the given bytes to the audio stream
*
* @param audioData audio data to write to the device
*
* @return <code>true</code> of the write completed successfully,
* <code> false </code>if the write was cancelled.
*/
public boolean write(byte[] audioData) {
return write(audioData, 0, audioData.length);
}
/**
* Writes the given bytes to the byte linear data buffer
*
* @param audioData audio data to write to the device
* @param offset the offset into the buffer
* @param size the number of bytes to write.
*
* @return <code>true</code> of the write completed successfully,
* <code> false </code>if the write was cancelled.
*/
public boolean write(byte[] audioData, int offset, int size) {
/*
* get a new buffer which will hold all of the data
*/
byte[] b = new byte[bytesPlayed + audioData.length];
for (int i = 0; i < bytesPlayed; i++) {
b[i] = byteLinearData[i]; // copy old data
}
for (int i = 0; i < size; i++) {
b[i + bytesPlayed] = audioData[i + offset]; // new data
}
byteLinearData = b;
bytesPlayed += size;
if (Logger.logLevel >= Logger.LOG_DETAIL) {
Logger.println("bytesPlayed " + bytesPlayed);
}
return true;
}
/**
* Return the data in an int array.
* @return data int[] voice data
*/
public int[] getLinearData() {
return AudioConversion.bytesToInts(byteLinearData);
}
}