All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sunlabs.brazil.slim.TTSServerHandler Maven / Gradle / Ivy

The newest version!
/*
 * TTSServerHandler.java
 *
 * Brazil project web application toolkit,
 * export version: 2.3 
 * Copyright (c) 2002-2006 Sun Microsystems, Inc.
 *
 * Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License Version 
 * 1.0 (the "License"). You may not use this file except in compliance with 
 * the License. A copy of the License is included as the file "license.terms",
 * and also available at http://www.sun.com/
 * 
 * The Original Code is from:
 *    Brazil project web application toolkit release 2.3.
 * The Initial Developer of the Original Code is: lc138592.
 * Portions created by lc138592 are Copyright (C) Sun Microsystems, Inc.
 * All Rights Reserved.
 * 
 * Contributor(s): lc138592, suhler.
 *
 * Version:  2.2
 * Created by lc138592 on 02/07/29
 * Last modified by suhler on 06/11/13 15:31:21
 *
 * Version Histories:
 *
 * 2.2 06/11/13-15:31:21 (suhler)
 *   MatchString moved packages from "handler" to "util"
 *
 * 2.1 02/10/01-16:36:03 (suhler)
 *   version change
 *
 * 1.9 02/08/12-15:36:27 (lc138592)
 *   Javadocs
 *
 * 1.8 02/08/12-15:13:55 (lc138592)
 *   Changed how the server is setup, now needs voice= option and provides two defaults.
 *
 * 1.7 02/08/07-16:37:31 (lc138592)
 *   Added scaling with the scale query/config parameter
 *
 * 1.6 02/08/07-14:50:39 (lc138592)
 *   Added an option to swap the bytes or not, PCM16 for au works, not for wav on Solaris though
 *
 * 1.5 02/08/06-17:56:22 (lc138592)
 *   Added byte swapping for 16-bit values, this is a temp fix
 *
 * 1.4 02/08/06-11:24:42 (suhler)
 *   remove * imports
 *   add "mustHave" iption
 *
 * 1.3 02/08/05-17:02:25 (lc138592)
 *   Added Javadocs
 *
 * 1.2 02/07/31-13:57:05 (lc138592)
 *   Added support to specify the response type, WAV, AU or raw
 *
 * 1.2 70/01/01-00:00:02 (Codemgr)
 *   SunPro Code Manager data about conflicts, renames, etc...
 *   Name history : 1 0 slim/TTSServerHandler.java
 *
 * 1.1 02/07/29-16:28:49 (lc138592)
 *   date and time created 02/07/29 16:28:49 by lc138592
 *
 */

package sunlabs.brazil.slim;

import com.sun.speech.freetts.Voice;
import com.sun.speech.freetts.audio.AudioPlayer;
import com.sun.speech.freetts.en.us.CMULexicon;
import java.util.Hashtable;
import javax.sound.sampled.AudioFormat;
import sunlabs.brazil.util.MatchString;
import sunlabs.brazil.server.Handler;
import sunlabs.brazil.server.Request;
import sunlabs.brazil.server.Server;
import sunlabs.brazil.slim.BasicAudioTools;
// import sunlabs.brazil.util.http.HttpInputStream;
// import sunlabs.brazil.util.regexp.Regexp;

 /**
 * This class provides an interface to a FreeTTS text to speech engine that 
 * resides locally with the server (as part of brazil).  An alternative to the
 * intrinsically high bandwidth Free TTS server, this was developed to allow the
 * expensive processing to be done in the server space.
 *
 * Like TTSHandler this utilizes the following query parameters:

*

  • transtext = text to be translated *

    * Configuration File Parameters: *

    *
    prefix, suffix, glob, match *
    Specify the URL that triggers this handler. * (See {@link sunlabs.brazil.handler.MatchString}). *
    mustHave
    *
    If defined, it specifies a variable that must be set in the * request properties in order for this handler to run. *
    encoding
    *
    specify one of ML8 (8-bit mulaw 8kHz), PCM8 (16-bit linear PCM, 8-kHz), PCM16 (.16-bit linear PCM, 16-kHz)
    *
    format
    *
    specify one of "wav" or "au" for Microsoft AIFF Wave format, or Sun Audio format
    *
    * * @author Leo Chao * @version @(#)TTSServerHandler.java 2.2 */ public class TTSServerHandler implements Handler { private Voice cVoice; private String cVoiceClassName; private String cVoiceDatabaseName; private String cPre; private Server cSer; private String cFormat; private String cEncoding; private int cML8Scaling; private boolean cSwap; private int cSampling; private MatchString cMatchString; private String cMustHave; private HTMLAudioPlayer htmlAudioPlayer; private Voice loadVoice(String voiceClassName, String databaseName) { Voice voice = null; Class voiceClass; try { voiceClass = Class.forName(voiceClassName); voice = (Voice) voiceClass.newInstance(); cSer.log(Server.LOG_DIAGNOSTIC, this, "voice initialized"); } catch (Exception e) { cSer.log(Server.LOG_ERROR, this, "Cannot initialize voice with: " + voiceClassName); cSer.log(Server.LOG_ERROR, this, "Exception thrown:\n"+e); } if (voice != null) { cSer.log(Server.LOG_DIAGNOSTIC, this, "database is: " + databaseName); voice.setLexicon(new CMULexicon()); voice.getFeatures().setString(Voice.DATABASE_NAME, databaseName); voice.load(); } return voice; } /** * Initializes the handler and the HTML Audio Player for transferring the data * back to the user. Checks for the voice8 and voice16 defaults, if prefix.voice * is set to these the system will provide a default 8 or 16 kHz voice. Otherwise * voice.class, voice.db and voice.sampling will need to be set. * * @param server {@link sunlabs.brazil.server.Server Server} object provided by the webserver. * @param prefix String indicating the URL-prefix expected for processing * @return Returns true if the TTS server parameters are present */ public boolean init(Server server, String prefix) { String voiceTag; cPre = prefix; cSer = server; cMatchString = new MatchString(prefix, server.props); cMustHave = server.props.getProperty(prefix + "mustHave"); htmlAudioPlayer = new HTMLAudioPlayer(); // Initialize voices voiceTag = server.props.getProperty(prefix+"voice", "voice8"); if (voiceTag.equals("voice8")) { cVoiceClassName = "com.sun.speech.freetts.en.us.CMUDiphoneVoice"; cVoiceDatabaseName = "cmu_kal/diphone_units.bin"; cSampling = 8000; cSer.log(Server.LOG_DIAGNOSTIC, "TTS init: ", "Using 8kHz Voice"); } else if (voiceTag.equals("voice16")) { cVoiceClassName = "com.sun.speech.freetts.en.us.CMUDiphoneVoice"; cVoiceDatabaseName = "cmu_kal/diphone_units16.bin"; cSampling = 16000; cSer.log(Server.LOG_DIAGNOSTIC, "TTS init: ", "Using 16kHz Voice"); } else { cVoiceClassName = server.props.getProperty(voiceTag+".class"); if (cVoiceClassName == null) { cSer.log(Server.LOG_ERROR, "TTS init: ", "Could not load voice class for voice " + voiceTag); return false; } cVoiceDatabaseName = server.props.getProperty(voiceTag+".db"); if (cVoiceDatabaseName == null) { cSer.log(Server.LOG_ERROR, "TTS init: ", "Could not load voice database for voice " + voiceTag); return false; } try { cSampling=Integer.parseInt(server.props.getProperty(voiceTag+".sampling")); } catch (Exception e) { cSer.log(Server.LOG_WARNING, "TTS init: ", "sampling not an integer"); cSampling=8000; } cSer.log(Server.LOG_DIAGNOSTIC, "TTS init: ", "Using Custom Voice:\nclass="+cVoiceClassName+"\ndatabase="+cVoiceDatabaseName+"\nsampling="+cSampling); } cVoice = loadVoice(cVoiceClassName, cVoiceDatabaseName); return true; } private void overrideDefaults(Request req) { String format, encoding, swap, scale; format = (String)((req.getQueryData()).get("format")); if (format == null) { format = (req.props.getProperty(cPre+"format", BasicAudioTools.AU_FILE)); } cFormat = format; encoding = (String)((req.getQueryData()).get("encoding")); if (encoding == null) { encoding = (req.props.getProperty(cPre+"encoding", "ML8")); //valid such is: 8-bit Mulaw (ML8), 16 bit PCM (PCM16) } cEncoding = encoding; swap = (String)((req.getQueryData()).get("swap")); if (swap == null) cSwap = false; else cSwap = true; scale = (String)((req.getQueryData()).get("scale")); if (scale == null) { scale = req.props.getProperty(cPre+"scale", "3"); } try { cML8Scaling = Integer.parseInt(scale.trim()); } catch (Exception e) { cML8Scaling = 3; } } /** * Responds to a text to speech request. Query parameter overriding is done * for:

    *

  • encoding: ML8, PCM16 *
  • format:au, wav *
  • swap: non-null *
  • scale: valid only for ML8 encoding, scales the conversion bitshift *

    * If successful an audio file response is sent back to the page, allowing * the browser/client to fire off plug-ins/programs to play the clip. * * @param req {@link sunlabs.brazil.server.Request Request} object passed in by the webserver. * @return Returns true if an audio file is successfully returned. */ public boolean respond(Request req) { String text; if(!isMyRequest(req)) { return false; } //Okay this is redundant and could be placed in isMyRequest as a side effect //but oh well. text = (String)((req.getQueryData()).get("transtext")); if (text.equals("")) { cSer.log(Server.LOG_WARNING,": TTS Error","Empty String, NOT processing"); return false; } overrideDefaults(req); cSer.log(Server.LOG_DIAGNOSTIC, ": TTS Text", text); cSer.log(Server.LOG_DIAGNOSTIC, ": TTS Format", cFormat); cSer.log(Server.LOG_DIAGNOSTIC, ": TTS Encoding", cEncoding); if (cVoice != null) { htmlAudioPlayer.setRequest(req); cVoice.setAudioPlayer(htmlAudioPlayer); text = text.replace('.', ';'); cVoice.speak(text); } else { cSer.log(Server.LOG_ERROR, this, "No voice to speak with!"); } return true; } private boolean isMyRequest(Request req) { if (!cMatchString.match(req.url)) return false; if (cMustHave != null && (req.props.getProperty(cMustHave)==null)) { req.log(Server.LOG_DIAGNOSTIC, cPre, cMustHave + " not set"); return false; } String text = (String)((req.getQueryData()).get("transtext")); if (text == null) return false; return true; } /* * HTML Audio Player - probably should be a seperate class since its * so big, but oh well */ private class HTMLAudioPlayer implements AudioPlayer { private AudioFormat audioFormat; private int bytesToPlay = 0; private int bytesPlayed = 0; private boolean firstByteSent = false; private long firstByteTime = -1; private Request cR; private String filetype; public void setRequest(Request req) { cR = req; } public void setAudioFormat(AudioFormat format) { this.audioFormat = format; } public AudioFormat getAudioFormat() { return this.audioFormat; } public void pause() {} public void resume() {} public void reset() {} public boolean drain() { return true; } public void begin(int size) { String type = null; byte[] audiohead = null; firstByteSent = false; // 8-bit mu-law at 8khz, will require conversion if (cEncoding.equals(BasicAudioTools.ML8)) size = size /2; bytesToPlay = size; if (cFormat.equals(BasicAudioTools.WAV_FILE)) { audiohead = BasicAudioTools.getWaveHeader(size, cEncoding, cSampling); cSer.log(Server.LOG_DIAGNOSTIC, this, "header size: " + audiohead.length + " encoding: " + cEncoding); type = "audio/wav"; } else if (cFormat.equals(BasicAudioTools.AU_FILE)) { audiohead = BasicAudioTools.getBasicHeader(size,cEncoding, cSampling); cSer.log(Server.LOG_DIAGNOSTIC, this, "header size: " + audiohead.length + " encoding: " + cEncoding); type = "audio/basic"; } else { type = "application/octet-stream"; audiohead = new byte[0]; } try { cR.sendHeaders(200, type, size + audiohead.length); cR.out.write(audiohead, 0, audiohead.length); cSer.log(Server.LOG_INFORMATIONAL, this, "Begin: " + size); } catch (Exception e) { cSer.log(Server.LOG_ERROR, this, "Cannot send response to server"); } } public void startFirstSampleTimer() { } public boolean end() { cSer.log(Server.LOG_DIAGNOSTIC, this, "ending audio player"); if (bytesPlayed < bytesToPlay) { int bytesNotPlayed = bytesToPlay - bytesPlayed; writeZero(bytesNotPlayed); cSer.log(Server.LOG_DIAGNOSTIC,this, bytesNotPlayed + " not played."); } bytesToPlay = 0; bytesPlayed = 0; return true; } public void cancel() {} public void close() {} public float getVolume() { return -1; } public void setVolume(float volume) {} public long getTime() { return -1; } public void resetTime() {} public boolean writeZero(int numZeros) { cSer.log(Server.LOG_INFORMATIONAL, ": writing voice", "Writing " + numZeros + " zeros"); try { if (numZeros > 0) cR.out.write(new byte[numZeros], 0, numZeros); else return false; } catch (Exception e) { cSer.log(Server.LOG_ERROR, this, "Cannot write to server"); return false; } return true; } public boolean write(byte[] audioData) { return write(audioData, 0, audioData.length); } public boolean write(byte[] audioData, int offset, int size) { cSer.log(Server.LOG_DIAGNOSTIC, this, "offset: " + offset + " size: " + size); if (!firstByteSent) { firstByteTime = System.currentTimeMillis(); firstByteSent = true; } //If we desire 8-bit mulaw, convert if (cEncoding.equals(BasicAudioTools.ML8)) { audioData = BasicAudioTools.convertToML8(audioData,size, cML8Scaling); size = size /2; cSer.log(Server.LOG_DIAGNOSTIC, this, "performing conversion w/ scaling of " + cML8Scaling); } if (cFormat.equals(BasicAudioTools.WAV_FILE)) { audioData = BasicAudioTools.swapBytes(audioData, offset, size); cSer.log(Server.LOG_DIAGNOSTIC, this, "wave file, swapping bytes"); } if (cSwap) { audioData = BasicAudioTools.swapBytes(audioData, offset, size); cSer.log(Server.LOG_DIAGNOSTIC, this, "swapping bytes"); } bytesPlayed += size; try { cR.out.write(audioData, offset, size); cSer.log(Server.LOG_INFORMATIONAL, ": writing voice", "sent " + size + " bytes" + " total: " + bytesPlayed); } catch (Exception e) { cSer.log(Server.LOG_ERROR, this, "Cannot send data to server"); } return true; } public void showMetrics() {} public long getFirstByteSentTime() { return firstByteTime; } } }





  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy