diff --git a/external/marytts-5.1.2/LICENSE.txt b/external/marytts-5.1.2/LICENSE.txt new file mode 100644 index 00000000..01dc74eb --- /dev/null +++ b/external/marytts-5.1.2/LICENSE.txt @@ -0,0 +1,74 @@ +MARY Software User Agreement +11 April 2011 + +MARY is licensed under the following terms. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . + + + + +Applicable Licenses + +MARY is built upon a number of other open source technologies and products. +Here is a list of those products with links to their licenses. + +hts_engine: the HMM-based speech synthesis code in MARY TTS is based on HTS, ported to Java by DFKI. The original HTS can be obtained from +http://hts-engine.sourceforge.net/ -- it is released under the New and +Simplified BSD License. + +freetts: MARY uses code from FreeTTS (http://freetts.sf.net) for various +processing modules and as the source of one method for waveform synthesis. +FreeTTS is licensed under the (BSD-style) FreeTTS license, see +doc/licenses/freetts-license.txt. + +JTok: The JTok tokenizer from http://heartofgold.dfki.de is distributed +under the GNU Lesser General Public License, see http://www.gnu.org or +doc/licenses/LGPL.txt. + +jsresources.jar: A few utility classes from http://www.jsresources.org +are distributed under the terms of the jsresources license, see +doc/licenses/jsresources-license.txt. + +log4j: MARY uses log4j (http://logging.apache.org/log4j) as a logging +mechanism. log4j is distributed under the Apache Software License, see +http://www.apache.org or doc/licenses/apache-software-license.txt + +JUnit: For unit testing of the java source, mary uses JUnit +(http://junit.org). JUnit is licensed under the Common Public License, see +http://junit.org or doc/licenses/CPL.txt. + +java-diff: A java diff implementation from http://www.incava.org/projects/java-diff for input-output-comparisons in the +Mary Expert Interface. java-diff is licensed under the GNU Lesser General +Public License, see http://www.gnu.org or doc/licenses/LGPL.txt. + +fast-md5: A fast md5 checksum implementation from http://www.twmacinta.com/myjava/fast_md5.php +used for computing checksums after downloading voices. fast-md5 is licensed under +the GNU Lesser General Public License, see http://www.gnu.org or doc/licenses/LGPL.txt. + +JavaOpenAIR: MARY can optionally be used as an OpenAIR component, +building on the JavaOpenAIR reference implementation from +http://www.mindmakers.org, which is licensed under the +(BSD-style) JavaOpenAIR license, see doc/licenses/JavaOpenAIR-license.txt +(files concerned: JavaOpenAIR.jar) + +mwdumper: A tool for extracting sets of pages from a MediaWiki dump file. +mwdumper is MIT-style like licensed, see http://www.mediawiki.org/wiki/Mwdumper +and for the license http://en.wikipedia.org/wiki/MIT_License. +(files concerned: mwdumper-2008-04-13.jar) + + +sgt: The Scientific Graphics Toolkit (sgt) is provided by the NOAA/PMEL/EPIC group (see http://www.epic.noaa.gov/java/sgt/) under the BSD-style EPIC license, see doc/licenses/epic-license.txt. + +IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS +AND CONDITIONS PRIOR TO USE OF THIS CONTENT. diff --git a/external/marytts-5.1.2/bin/marytts-client b/external/marytts-5.1.2/bin/marytts-client new file mode 100644 index 00000000..f20b9315 --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-client @@ -0,0 +1,10 @@ +#!/bin/bash +########################################################################## +# MARY TTS client +########################################################################## + +# Set the Mary base installation directory in an environment variable: +BINDIR="`dirname "$0"`" +export MARY_BASE="`(cd "$BINDIR"/.. ; pwd)`" + +java -showversion -ea -Dserver.host=localhost -Dserver.port=59125 -jar "$MARY_BASE/lib/marytts-client-5.1.2-jar-with-dependencies.jar" diff --git a/external/marytts-5.1.2/bin/marytts-client.bat b/external/marytts-5.1.2/bin/marytts-client.bat new file mode 100644 index 00000000..4a231d9f --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-client.bat @@ -0,0 +1,10 @@ +@echo off +set BINDIR=%~dp0 +call :RESOLVE "%BINDIR%\.." MARY_BASE + +java -showversion -ea -Dserver.host=localhost -Dserver.port=59125 -jar "%MARY_BASE%\lib\marytts-client-5.1.2-jar-with-dependencies.jar" +goto :EOF + +:RESOLVE +set %2=%~f1 +goto :EOF diff --git a/external/marytts-5.1.2/bin/marytts-component-installer b/external/marytts-5.1.2/bin/marytts-component-installer new file mode 100644 index 00000000..44cea75d --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-component-installer @@ -0,0 +1,5 @@ +#!/bin/sh +BINDIR="`dirname "$0"`" +export MARY_BASE="`(cd "$BINDIR"/.. ; pwd)`" +java -showversion -ea -Dmary.base="$MARY_BASE" $* -cp "$MARY_BASE/lib/*" marytts.tools.install.InstallerGUI + diff --git a/external/marytts-5.1.2/bin/marytts-component-installer.bat b/external/marytts-5.1.2/bin/marytts-component-installer.bat new file mode 100644 index 00000000..8f4ff657 --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-component-installer.bat @@ -0,0 +1,9 @@ +@echo off +set BINDIR=%~dp0 +call :RESOLVE "%BINDIR%\.." MARY_BASE +java -showversion -ea -Dmary.base="%MARY_BASE%" -cp ".;%MARY_BASE%\lib\*" marytts.tools.install.InstallerGUI +goto :EOF + +:RESOLVE +set %2=%~f1 +goto :EOF diff --git a/external/marytts-5.1.2/bin/marytts-server b/external/marytts-5.1.2/bin/marytts-server new file mode 100644 index 00000000..be3c0939 --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-server @@ -0,0 +1,11 @@ +#!/bin/bash +########################################################################## +# MARY TTS server +########################################################################## + +# Set the Mary base installation directory in an environment variable: +BINDIR="`dirname "$0"`" +export MARY_BASE="`(cd "$BINDIR"/.. ; pwd)`" + + +java -showversion -ea -Xms40m -Xmx1g -cp "$MARY_BASE/lib/*" -Dmary.base="$MARY_BASE" $* marytts.server.Mary diff --git a/external/marytts-5.1.2/bin/marytts-server.bat b/external/marytts-5.1.2/bin/marytts-server.bat new file mode 100644 index 00000000..f3cc2dad --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-server.bat @@ -0,0 +1,14 @@ +@echo off + +rem Set the Mary base installation directory in an environment variable: +set BINDIR=%~dp0 + +call :RESOLVE "%BINDIR%\.." MARY_BASE + +set CLASSPATH=".;%MARY_BASE%\lib\*" +java -showversion -ea -Xms40m -Xmx1g -cp %CLASSPATH% "-Dmary.base=%MARY_BASE%" marytts.server.Mary +goto :EOF + +:RESOLVE +set %2=%~f1 +goto :EOF diff --git a/external/marytts-5.1.2/doc/examples/client/MaryClient.py b/external/marytts-5.1.2/doc/examples/client/MaryClient.py new file mode 100644 index 00000000..46569873 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/MaryClient.py @@ -0,0 +1,367 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import socket, sys, types, getopt + + +languageNames = {'de':'German', + 'en':'English', + 'en_US':'US English', + 'tib':'Tibetan'} + +class MaryClient: + specificationVersion = "0.1" + + """Python implementation of a MARY TTS client""" + def __init__( self, host="cling.dfki.uni-sb.de", port=59125, profile=False, quiet=False ): + self.host = host + self.port = port + self.profile = profile + self.quiet = quiet + self.allVoices = None # array of Voice objects + self.voicesByLocaleMap = {} # Map locale strings to arrays of Voice objects + self.allDataTypes = None # array of DataType objects + self.inputDataTypes = None # array of DataType objects + self.outputDataTypes = None # array of DataType objects + self.serverExampleTexts = {} + self.voiceExampleTexts = {} + self.serverVersionInfo = u'' + + if not self.quiet: + sys.stderr.write( "MARY TTS Python Client %s\n" % ( self.specificationVersion ) ) + try: + info = self.getServerVersionInfo() + except: + sys.stderr.write( "Problem connecting to mary server at %s:%i\n" % ( self.host, self.port ) ) + raise + sys.stderr.write( "Connected to %s:%i, " % ( self.host, self.port ) ) + sys.stderr.write( info ) + sys.stderr.write( '\n' ) + + def __getServerInfo( self, request="", marySocket=None ): + """Get answer to request from mary server. Returns a list of unicode strings, + each representing a line without the line break. + """ + closeSocket = False + if marySocket is None: + closeSocket = True + marySocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + marySocket.connect( ( self.host, self.port ) ) + assert isinstance(marySocket, socket.SocketType) + maryFile = marySocket.makefile( 'rwb', 1 ) # read-write, line-buffered + maryFile.write( unicode( request+"\n" ).encode( 'utf-8' ) ) + result = [] + while True: + got = unicode( maryFile.readline().strip(), 'utf-8' ) + # read until end of file or an empty line is read: + if not got: break + result.append(got) + if closeSocket: + marySocket.close() + return result + + def getServerVersionInfo( self ): + "Get version info from server. Returns a unicode string" + if self.serverVersionInfo == u'': + # need to get it from server + self.serverVersionInfo = u'\n'.join(self.__getServerInfo("MARY VERSION")) + return self.serverVersionInfo + + def getAllDataTypes(self, locale=None): + """Obtain a list of all data types known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an array of DataType objects + """ + if self.allDataTypes is None: + self.__fillDataTypes() + assert self.allDataTypes is not None and len( self.allDataTypes ) > 0 + if locale is None: + return self.allDataTypes + else: + assert isinstance(locale, types.UnicodeType), "Unexpected type for locale: '%s'" % (type(locale)) + return [d for d in self.allDataTypes if d.locale is None or d.locale == locale] + + def getInputDataTypes(self,locale=None): + """Obtain a list of input data types known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an arry of DataType objects + """ + if self.inputDataTypes is None: + self.__fillDataTypes() + assert self.inputDataTypes is not None and len( self.inputDataTypes ) > 0 + if locale is None: + return self.inputDataTypes + else: + assert isinstance(locale, types.UnicodeType), "Unexpected type for locale: '%s'" % (type(locale)) + return [d for d in self.inputDataTypes if d.locale is None or d.locale == locale] + + def getOutputDataTypes(self, locale=None): + """Obtain a list of output data types known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an arry of DataType objects + """ + if self.outputDataTypes is None: + self.__fillDataTypes() + assert self.outputDataTypes is not None and len( self.outputDataTypes ) > 0 + if locale is None: + return self.outputDataTypes + else: + assert isinstance(locale, types.UnicodeType), "Unexpected type for locale: '%s'" % (type(locale)) + return [d for d in self.outputDataTypes if d.locale is None or d.locale == locale] + + + def __fillDataTypes( self ): + self.allDataTypes = [] + self.inputDataTypes = [] + self.outputDataTypes = [] + marySocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + marySocket.connect( ( self.host, self.port ) ) + # Expect a variable number of lines of the kind + # RAWMARYXML INPUT OUTPUT + # TEXT_DE LOCALE=de INPUT + # AUDIO OUTPUT + typeStrings = self.__getServerInfo( "MARY LIST DATATYPES", marySocket ) + if not typeStrings or len(typeStrings) == 0: + raise IOError( "Could not get list of data types from Mary server" ) + marySocket.close() + for typeString in typeStrings: + parts = typeString.split() + if len( parts ) == 0: + continue + name = parts[0] + isInputType = False + isOutputType = False + locale = None + for part in parts[1:]: + if part[:7] == "LOCALE=": + locale = part[7:] + elif part == "INPUT": + isInputType = True + elif part == "OUTPUT": + isOutputType = True + dt = DataType( name, locale, isInputType, isOutputType ) + self.allDataTypes.append( dt ) + if dt.isInputType: + self.inputDataTypes.append( dt ) + if dt.isOutputType: + self.outputDataTypes.append( dt ) + + def getVoices( self, locale=None ): + """Obtain a list of voices known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an array of Voice objects + """ + if self.allVoices is None: + self.__fillVoices() + assert self.allVoices is not None and len( self.allVoices ) > 0 + if locale is None: + return self.allVoices + else: + assert isinstance(locale, types.UnicodeType), "Unexpected type for locale: '%s'" % (type(locale)) + if self.voicesByLocaleMap.has_key(locale): + return self.voicesByLocaleMap[locale] + else: + raise Exception("No voices for locale '%s'" % (locale)) + + def __fillVoices( self ): + self.allVoices = [] + self.voicesByLocaleMap = {} + marySocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + marySocket.connect( ( self.host, self.port ) ) + # Expect a variable number of lines of the kind + # de7 de female + # us2 en male + # dfki-stadium-emo de male limited + voiceStrings = self.__getServerInfo( "MARY LIST VOICES", marySocket ) + if not voiceStrings or len(voiceStrings) == 0: + raise IOError( "Could not get list of voices from Mary server" ) + marySocket.close() + for voiceString in voiceStrings: + parts = voiceString.split() + if len( parts ) < 3: + continue + name = parts[0] + locale = parts[1] + gender = parts[2] + domain = None + if len( parts ) > 3: + domain = parts[3] + voice = Voice( name, locale, gender, domain ) + self.allVoices.append( voice ) + localeVoices = None + if self.voicesByLocaleMap.has_key( locale ): + localeVoices = self.voicesByLocaleMap[locale] + else: + localeVoices = [] + self.voicesByLocaleMap[locale] = localeVoices + localeVoices.append( voice ) + + def getGeneralDomainVoices( self, locale=None ): + """Obtain a list of general domain voices known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an array of Voice objects + """ + return [v for v in self.getVoices( locale ) if not v.isLimitedDomain] + + def getLimitedDomainVoices( self, locale=None ): + """Obtain a list of limited domain voices known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an array of Voice objects + """ + return [v for v in self.getVoices( locale ) if v.isLimitedDomain] + + def getAvailableLanguages(self): + """ Check available voices and return a list of tuples (abbrev, name) + representing the available languages -- e.g. [('en', 'English'),('de', 'German')]. + """ + if self.allVoices is None: + self.__fillVoices() + assert self.allVoices is not None and len( self.allVoices ) > 0 + languages = [] + for l in self.voicesByLocaleMap.keys(): + if languageNames.has_key(l): + languages.append((l,languageNames[l])) + else: + languages.append((l, l)) + return languages + + def getServerExampleText( self, dataType ): + """Request an example text for a given data type from the server. + dataType the string representation of the data type, + e.g. "RAWMARYXML". This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities.""" + if not self.serverExampleTexts.has_key( dataType ): + exampleTexts = self.__getServerInfo( "MARY EXAMPLETEXT %s" % ( dataType ) ) + if not exampleTexts or len(exampleTexts) == 0: + raise IOError( "Could not get example text for type '%s' from Mary server" % (dataType)) + exampleText = u'\n'.join(exampleTexts) + self.serverExampleTexts[dataType] = exampleText + return self.serverExampleTexts[dataType] + + def process( self, input, inputType, outputType, audioType=None, defaultVoiceName=None, output=sys.stdout ): + assert type( input ) in types.StringTypes + assert type( inputType ) in types.StringTypes + assert type( outputType ) in types.StringTypes + assert audioType is None or type( audioType ) in types.StringTypes + assert defaultVoiceName is None or type( defaultVoiceName ) in types.StringTypes + assert callable( getattr( output, 'write' ) ) + if type( input ) != types.UnicodeType: + input = unicode( input, 'utf-8' ) + maryInfoSocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + maryInfoSocket.connect( ( self.host, self.port ) ) + assert type( maryInfoSocket ) is socket.SocketType + maryInfo = maryInfoSocket.makefile( 'rwb', 1 ) # read-write, line-buffered + maryInfo.write( unicode( "MARY IN=%s OUT=%s" % ( inputType, outputType ), 'utf-8' ) ) + if audioType: + maryInfo.write( unicode( " AUDIO=%s" % ( audioType ), 'utf-8' ) ) + if defaultVoiceName: + maryInfo.write( unicode( " VOICE=%s" % ( defaultVoiceName ), 'utf-8' ) ) + maryInfo.write( "\r\n" ) + # Receive a request ID: + id = maryInfo.readline() + maryDataSocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + maryDataSocket.connect( ( self.host, self.port ) ) + assert type( maryDataSocket ) is socket.SocketType + maryDataSocket.sendall( id ) # includes newline + maryDataSocket.sendall( input.encode( 'utf-8' ) ) + maryDataSocket.shutdown( 1 ) # shutdown writing + # Set mary info socket to non-blocking, so we only read somthing + # if there is something to read: + maryInfoSocket.setblocking( 0 ) + while True: + try: + err = maryInfoSocket.recv( 8192 ) + if err: sys.stderr.write( err ) + except: + pass + got = maryDataSocket.recv( 8192 ) + if not got: break + output.write( got ) + maryInfoSocket.setblocking( 1 ) + while True: + err = maryInfoSocket.recv( 8192 ) + if not err: break + sys.stderr.write( err ) + + + +################ data representation classes ################## + +class DataType: + def __init__( self, name, locale=None, isInputType=False, isOutputType=False ): + self.name = name + self.locale = locale + self.isInputType = isInputType + self.isOutputType = isOutputType + + def isTextType( self ): + return self.name != "AUDIO" + +class Voice: + + def __init__( self, name, locale, gender, domain="general" ): + self.name = name + self.locale = locale + self.gender = gender + self.domain = domain + if not domain or domain == "general": + self.isLimitedDomain = False + else: + self.isLimitedDomain = True + + def __str__(self): + if languageNames.has_key(self.locale): + langName = languageNames[self.locale] + else: + langName = self.locale + if self.isLimitedDomain: + return "%s (%s, %s %s)" % (self.name, self.domain, langName, self.gender) + else: + return "%s (%s %s)" % (self.name, langName, self.gender) + +##################### Main ######################### + +if __name__ == '__main__': + + serverHost = "cling.dfki.uni-sb.de" + serverPort = 59125 + inputType = "TEXT" + outputType = "AUDIO" + audioType = "WAVE" + defaultVoice = None + inputEncoding = 'utf-8' + ( options, rest ) = getopt.getopt( sys.argv[1:], '', \ + ['server.host=', 'server.port=', 'input.type=', 'output.type=', \ + 'audio.type=', 'voice.default=', 'input.encoding='] ) + for ( option, value ) in options: + if option == '--server.host': serverHost = value + elif option == '--server.port': serverPort = int( value ) + elif option == '--input.type': inputType = value + elif option == '--output.type': outputType = value + elif option == '--audio.type': audioType = value + elif option == '--voice.default': defaultVoice = value + elif option == '--input.encoding': inputEncoding = value + if len( rest )>0: # have input file + inputFile = file( rest[0] ) + else: + inputFile = sys.stdin + input = unicode( ''.join( inputFile.readlines() ), inputEncoding ) + if len( rest )>1: # also have output file + outputFile = file( rest[1] ) + else: + outputFile = sys.stdout + + maryClient = MaryClient( serverHost, serverPort ) + maryClient.process( input, inputType, outputType, audioType, defaultVoice, outputFile ) diff --git a/external/marytts-5.1.2/doc/examples/client/MaryClientUser.java b/external/marytts-5.1.2/doc/examples/client/MaryClientUser.java new file mode 100644 index 00000000..a05246cc --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/MaryClientUser.java @@ -0,0 +1,102 @@ +/** + * Copyright 2000-2006 DFKI GmbH. + * All Rights Reserved. Use is subject to license terms. + * + * Permission is hereby granted, free of charge, to use and distribute + * this software and its documentation without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of this work, and to + * permit persons to whom this work is furnished to do so, subject to + * the following conditions: + * + * 1. The code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Any modifications must be clearly marked as such. + * 3. Original authors' names are not deleted. + * 4. The authors' names are not used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE + * CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.UnknownHostException; +import java.util.Locale; + +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.LineEvent; +import javax.sound.sampled.LineListener; +import javax.sound.sampled.UnsupportedAudioFileException; + +import marytts.util.data.audio.AudioPlayer; +import marytts.client.MaryClient; +import marytts.util.http.Address; + +/** + * A demo class illustrating how to use the MaryClient class. + * This will connect to a MARY server, version 4.x. + * It requires maryclient.jar from MARY 4.0. + * This works transparently with MARY servers in both http and socket server mode. + * + * Compile this as follows: + * javac -cp maryclient.jar MaryClientUser.java + * + * And run as: + * java -cp .:maryclient.jar MaryClientUser + * + * @author marc + * + */ + +public class MaryClientUser { + + public static void main(String[] args) + throws IOException, UnknownHostException, UnsupportedAudioFileException, + InterruptedException + { + String serverHost = System.getProperty("server.host", "cling.dfki.uni-sb.de"); + int serverPort = Integer.getInteger("server.port", 59125).intValue(); + MaryClient mary = MaryClient.getMaryClient(new Address(serverHost, serverPort)); + String text = "Willkommen in der Welt der Sprachsynthese!"; + // If the given locale is not supported by the server, it returns + // an ambigous exception: "Problem processing the data." + String locale = "de"; // or US English (en-US), Telugu (te), Turkish (tr), ... + String inputType = "TEXT"; + String outputType = "AUDIO"; + String audioType = "WAVE"; + String defaultVoiceName = null; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mary.process(text, inputType, outputType, locale, audioType, defaultVoiceName, baos); + // The byte array constitutes a full wave file, including the headers. + // And now, play the audio data: + AudioInputStream ais = AudioSystem.getAudioInputStream( + new ByteArrayInputStream(baos.toByteArray())); + LineListener lineListener = new LineListener() { + public void update(LineEvent event) { + if (event.getType() == LineEvent.Type.START) { + System.err.println("Audio started playing."); + } else if (event.getType() == LineEvent.Type.STOP) { + System.err.println("Audio stopped playing."); + } else if (event.getType() == LineEvent.Type.OPEN) { + System.err.println("Audio line opened."); + } else if (event.getType() == LineEvent.Type.CLOSE) { + System.err.println("Audio line closed."); + } + } + }; + + AudioPlayer ap = new AudioPlayer(ais, lineListener); + ap.start(); + } +} diff --git a/external/marytts-5.1.2/doc/examples/client/c++/Makefile b/external/marytts-5.1.2/doc/examples/client/c++/Makefile new file mode 100644 index 00000000..a609beda --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/c++/Makefile @@ -0,0 +1,45 @@ +########################################################################## +# Copyright (C) 2000-2006 DFKI GmbH. +# All rights reserved. Use is subject to license terms. +# +# Permission is hereby granted, free of charge, to use and distribute +# this software and its documentation without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of this work, and to +# permit persons to whom this work is furnished to do so, subject to +# the following conditions: +# +# 1. The code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Any modifications must be clearly marked as such. +# 3. Original authors' names are not deleted. +# 4. The authors' names are not used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE +# CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +# THIS SOFTWARE. +########################################################################## + +CC=g++ +CFLAGS=-Wall -w -O3 -g +ICUDIR=/usr/local/icu +ICULIBS=-Wl,-R,$(ICUDIR)/lib -L$(ICUDIR)/lib -licuuc -licui18n -ldl + +all: MaryDemo + +MaryDemo: MaryClient.o MaryDemo.o + $(CC) $(CFLAGS) *.o -o MaryDemo $(LIBS) + +%.o: %.cc + $(CC) $(CFLAGS) $(RFLAGS) -o $@ -c $< + +clean: + rm -rf *.o ./MaryDemo + diff --git a/external/marytts-5.1.2/doc/examples/client/c++/MaryClient.cc b/external/marytts-5.1.2/doc/examples/client/c++/MaryClient.cc new file mode 100644 index 00000000..7e920d95 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/c++/MaryClient.cc @@ -0,0 +1,277 @@ +/** + * Copyright 2000-2006 DFKI GmbH. + * All Rights Reserved. Use is subject to license terms. + * + * Permission is hereby granted, free of charge, to use and distribute + * this software and its documentation without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of this work, and to + * permit persons to whom this work is furnished to do so, subject to + * the following conditions: + * + * 1. The code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Any modifications must be clearly marked as such. + * 3. Original authors' names are not deleted. + * 4. The authors' names are not used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE + * CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ +#include +#include +#include +#include + +#include "MaryClient.h" + +using namespace std; + +/** + * A C++ implementation of a simple client to the MARY TTS system. + * result: an empty string serving as the container for the output. + * It will return text or audio data; text data will be encoded as UTF-8. + * inputText: the UTF-8 encoded text (or XML document) to send as a request + * maryInFormat: the input type of the data in inputText, e.g. TEXT + * maryOutFormat: the output type to produce, e.g. MBROLA, AUDIO + * locale: the language of the input, e.g. EN-US, DE + * audioType: for AUDIO output, the type of audio data to produce, + * e.g. WAVE or MP3. + * voice: the voice to be used, e.g. cmu-slt-hsmm, bits3. + * effects: the list of effects to be generated. + * return value: 0 on success, negative on failure. + */ +int +MaryClient::maryQuery( int server_port, + string server_host, + string& result, + string inputText, + string maryInFormat, + string maryOutFormat, + string locale, + string audioType, + string voice, + string effects ) { + + // prepare the request + string query = "MARY"; + query += " IN=" + maryInFormat; + query += " OUT=" + maryOutFormat; + query += " LOCALE=" + locale; // remove this line, if using an older version than MARY 4.0 + query += " AUDIO=" + audioType; + query += " VOICE=" + voice; + if (effects != "") { + query += " EFFECTS=" + effects; + } + query += "\012\015"; + + //cout << "Constructed query: " << query << endl; + + // declare connection stuff + struct sockaddr_in maryServer; + struct sockaddr_in maryClient; + struct hostent* hostInfo; + + // declare variables + int maryInfoSocket; + int maryDataSocket; + + // set configuration parameters + + // get host information + hostInfo = gethostbyname (server_host.c_str()); + + if (hostInfo == NULL) + { + return -2; + } + + + // create a tcp connection to the mary server + maryInfoSocket = socket (AF_INET, SOCK_STREAM, 0); + + // verify that the socket could be opened successfully + if (maryInfoSocket == -1) + { + return -2; + } + else + // autoflush stdout, bind and connect + { + maryClient.sin_family = AF_INET; + maryClient.sin_port = htons (0); + maryClient.sin_addr.s_addr = INADDR_ANY; + + int status = bind (maryInfoSocket, (struct sockaddr*) &maryClient, sizeof (maryClient)); + + if (status != 0) + { + return -2; + } + + maryServer.sin_family = AF_INET; + maryServer.sin_port = htons (server_port); + memcpy ((char*) &maryServer.sin_addr.s_addr, hostInfo->h_addr_list [0], hostInfo->h_length); + + status = connect (maryInfoSocket, (struct sockaddr*) &maryServer, sizeof (maryServer)); + + if (status != 0) + { + return -2; + } + } + + // send request to the Mary server + if (send (maryInfoSocket, query.c_str (), query.size (), 0) == -1) + { + return -2; + } + + + // receive the request id + char id [32] = ""; + + if (recv (maryInfoSocket, id, 32, 0) == -1) + { + return -2; + } + + //cout << "Read id: " << id << endl; + + // create a tcp connection to the mary server + maryDataSocket = socket (AF_INET, SOCK_STREAM, 0); + + // verify that the socket could be opened successfully + if (maryDataSocket == -1) + { + return -2; + } + else + // autoflush stdout, bind and connect + { + maryClient.sin_family = AF_INET; + maryClient.sin_port = htons (0); + maryClient.sin_addr.s_addr = INADDR_ANY; + + int status = bind (maryDataSocket, (struct sockaddr*) &maryClient, sizeof (maryClient)); + + if (status != 0) + { + return -2; + } + + maryServer.sin_family = AF_INET; + maryServer.sin_port = htons (server_port); + memcpy ((char*) &maryServer.sin_addr.s_addr, hostInfo->h_addr_list [0], hostInfo->h_length); + + status = connect (maryDataSocket, (struct sockaddr*) &maryServer, sizeof (maryServer)); + + if (status != 0) + { + return -2; + } + } + + + // send the request id to the Mary server + if (send (maryDataSocket, id, strlen (id), 0) == -1) + { + return -2; + } + + //cout << "Sending request: " << inputText << endl; + + // send the query to the Mary server + if (send (maryDataSocket, inputText.c_str (), inputText.size (), 0) == -1) + { + return -2; + } + + if (send (maryDataSocket, "\012\015", 2, 0) == -1) + { + return -2; + } + + + // shutdown data socket + shutdown (maryDataSocket, 1); + + + //cout << "Reading result" << endl; + + unsigned int total_bytes = 0; + int recv_bytes = 0; + char data [1024] = ""; + + result [0] = '\0'; + + // receive the request result + do + { + data [0] = '\0'; + + recv_bytes = recv (maryDataSocket, data, 1024, 0); + + if (recv_bytes == -1) + { + return -2; + } + else if (recv_bytes > 0) + { + //cout << "("</libwsock32.a +#include +#else +#include +#endif + +#include +#include +#include + +#include "MaryClient.h" + +using namespace std; + +/** + * A C++ implementation of a simple client to the MARY TTS system. + * result: an empty string serving as the container for the output. + * It will return text or audio data; text data will be encoded as UTF-8. + * inputText: the UTF-8 encoded text (or XML document) to send as a request + * maryInFormat: the input type of the data in inputText, e.g. TEXT + * maryOutFormat: the output type to produce, e.g. MBROLA, AUDIO + * locale: the language of the input, e.g. EN-US, DE + * audioType: for AUDIO output, the type of audio data to produce, + * e.g. WAVE or MP3. + * voice: the voice to be used, e.g. cmu-slt-hsmm, bits3. + * effects: the list of effects to be generated. + * return value: 0 on success, negative on failure. + */ +int +MaryClient::maryQuery( int server_port, + string server_host, + string& result, + string inputText, + string maryInFormat, + string maryOutFormat, + string locale, + string audioType, + string voice, + string effects ) { + + // prepare the request + string query = "MARY"; + query += " IN=" + maryInFormat; + query += " OUT=" + maryOutFormat; + query += " LOCALE=" + locale; // remove this line, if using an older version than MARY 4.0 + query += " AUDIO=" + audioType; + query += " VOICE=" + voice; + if (effects != "") { + query += " EFFECTS=" + effects; + } + query += "\012\015"; + + //cout << "Constructed query: " << query << endl; + + // declare connection stuff + struct sockaddr_in maryServer; + struct sockaddr_in maryClient; + struct hostent* hostInfo; + + // declare variables + int maryInfoSocket; + int maryDataSocket; + + // set configuration parameters + + // get host information + hostInfo = gethostbyname (server_host.c_str()); + + if (hostInfo == NULL) + { + return -2; + } + + + // create a tcp connection to the mary server + maryInfoSocket = socket (AF_INET, SOCK_STREAM, 0); + + // verify that the socket could be opened successfully + if (maryInfoSocket == -1) + { + return -2; + } + else + // autoflush stdout, bind and connect + { + maryClient.sin_family = AF_INET; + maryClient.sin_port = htons (0); + maryClient.sin_addr.s_addr = INADDR_ANY; + + int status = bind (maryInfoSocket, (struct sockaddr*) &maryClient, sizeof (maryClient)); + + if (status != 0) + { + return -2; + } + + maryServer.sin_family = AF_INET; + maryServer.sin_port = htons (server_port); + memcpy ((char*) &maryServer.sin_addr.s_addr, hostInfo->h_addr_list [0], hostInfo->h_length); + + status = connect (maryInfoSocket, (struct sockaddr*) &maryServer, sizeof (maryServer)); + + if (status != 0) + { + return -2; + } + } + + // send request to the Mary server + if (send (maryInfoSocket, query.c_str (), query.size (), 0) == -1) + { + return -2; + } + + + // receive the request id + char id [32] = ""; + + if (recv (maryInfoSocket, id, 32, 0) == -1) + { + return -2; + } + + //cout << "Read id: " << id << endl; + + // create a tcp connection to the mary server + maryDataSocket = socket (AF_INET, SOCK_STREAM, 0); + + // verify that the socket could be opened successfully + if (maryDataSocket == -1) + { + return -2; + } + else + // autoflush stdout, bind and connect + { + maryClient.sin_family = AF_INET; + maryClient.sin_port = htons (0); + maryClient.sin_addr.s_addr = INADDR_ANY; + + int status = bind (maryDataSocket, (struct sockaddr*) &maryClient, sizeof (maryClient)); + + if (status != 0) + { + return -2; + } + + maryServer.sin_family = AF_INET; + maryServer.sin_port = htons (server_port); + memcpy ((char*) &maryServer.sin_addr.s_addr, hostInfo->h_addr_list [0], hostInfo->h_length); + + status = connect (maryDataSocket, (struct sockaddr*) &maryServer, sizeof (maryServer)); + + if (status != 0) + { + return -2; + } + } + + + // send the request id to the Mary server + if (send (maryDataSocket, id, strlen (id), 0) == -1) + { + return -2; + } + + //cout << "Sending request: " << inputText << endl; + + // send the query to the Mary server + if (send (maryDataSocket, inputText.c_str (), inputText.size (), 0) == -1) + { + return -2; + } + + if (send (maryDataSocket, "\012\015", 2, 0) == -1) + { + return -2; + } + + + // shutdown data socket + shutdown (maryDataSocket, 1); + + + //cout << "Reading result" << endl; + + unsigned int total_bytes = 0; + int recv_bytes = 0; + char data [1024] = ""; + + result [0] = '\0'; + + // receive the request result + do + { + data [0] = '\0'; + + recv_bytes = recv (maryDataSocket, data, 1024, 0); + + if (recv_bytes == -1) + { + return -2; + } + else if (recv_bytes > 0) + { + //cout << "("< +#include +#include + +#include "MaryClient.h" + +using namespace std; + +/** + * Demonstration code for using the MaryClient. + + Call this as: + * ./MaryDemo + * or + * ./MaryDemo > output.wav + */ +int main() { + int server_port = 59125; + string server_host = "localhost"; + string inputText = "Welcome to the world of speech synthesis!"; + string maryInFormat = "TEXT"; + string maryOutFormat = "AUDIO"; + //string maryOutFormat = "REALISED_DURATIONS"; + string locale = "en-US"; + string audioType = "WAV_FILE"; + string voice = "cmu-slt-hsmm"; + string effects; +// effects += "Volume(amount:5.0;)+"; +// effects += "TractScaler(amount:1.5;)+"; +// effects += "F0Scale(f0Scale:2.0;)+"; +// effects += "F0Add(f0Add:50.0;)+"; +// effects += "Rate(durScale:1.5;)+"; +// effects += "Robot(amount:100.0;)+"; +// effects += "Whisper(amount:100.0;)+"; +// effects += "Stadium(amount:100.0)+"; +// effects += "Chorus(delay1:466;amp1:0.54;delay2:600;amp2:-0.10;delay3:250;amp3:0.30)+"; +// effects += "FIRFilter(type:3;fc1:500.0;fc2:2000.0)+"; +// effects += "JetPilot"; + string result; + + MaryClient maryClient; + maryClient.maryQuery( server_port, server_host, result, inputText, maryInFormat, maryOutFormat, locale, audioType, voice, effects); + + if (maryOutFormat == "AUDIO") { + // write result into a file + const char *filename = "output.wav"; + ofstream file( filename ); + file << result; + + // play output + //system("play output.wav"); + } else { + cout << "RESULT: " << endl << result << endl; + } + + return 0; +} + diff --git a/external/marytts-5.1.2/doc/examples/client/c++/README.txt b/external/marytts-5.1.2/doc/examples/client/c++/README.txt new file mode 100644 index 00000000..e38af989 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/c++/README.txt @@ -0,0 +1,4 @@ +Start MARY as a socket server: + +maryserver -Dserver=socket +(or change entry 'server' in conf/marybase.config) diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient-http.py b/external/marytts-5.1.2/doc/examples/client/maryclient-http.py new file mode 100644 index 00000000..cf9782e8 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient-http.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +import httplib, urllib + +# A basic mary client in Python, +# kindly donated to the MARY TTS project +# by Hugh Sasse. Thanks Hugh! + +# A very basic Python class for accessing +# the MARY TTS system using the modern +# HTTP server. +# Warning, this is probably ghastly Python, +# most of my time of late has been with +# other languages, so I'm not up to date +# with all the stylistic conventions of +# modern Python. +# This does seem to work OK though. + +class maryclient: + """A basic handler for MARY-TTS HTTP clients + + At present, there is no checking for + allowed voices, locales, and so on. + Most of the useful parameters can be + accessed by get_ and set_ methods. + Relying on winsound, this is Windows + specific. + """ + def __init__(self): + """Set up useful defaults (for + people in England, anyway)""" + self.host = "127.0.0.1" + self.port = 59125 + self.input_type = "TEXT" + self.output_type = "AUDIO" + self.audio = "WAVE_FILE" + self.locale = "en_GB" + self.voice = "dfki-prudence-hsmm" + + def set_host(self, a_host): + """Set the host for the TTS server.""" + self.host = a_host + + def get_host(self): + """Get the host for the TTS server.""" + self.host + + def set_port(self, a_port): + """Set the port for the TTS server.""" + self.port = a_port + + def get_port(self): + """Get the port for the TTS server.""" + self.port + + def set_input_type(self, type): + """Set the type of input being + supplied to the TTS server + (such as 'TEXT').""" + self.input_type = type + + def get_input_type(self): + """Get the type of input being + supplied to the TTS server + (such as 'TEXT').""" + self.input_type + + def set_output_type(self, type): + """Set the type of input being + supplied to the TTS server + (such as 'AUDIO').""" + self.output_type = type + + def get_output_type(self): + """Get the type of input being + supplied to the TTS server + (such as "AUDIO").""" + self.output_type + + def set_locale(self, a_locale): + """Set the locale + (such as "en_GB").""" + self.locale = a_locale + + def get_locale(self): + """Get the locale + (such as "en_GB").""" + self.locale + + def set_audio(self, audio_type): + """Set the audio type for playback + (such as "WAVE_FILE").""" + self.audio = audio_type + + def get_audio(self): + """Get the audio type for playback + (such as "WAVE_FILE").""" + self.audio + + def set_voice(self, a_voice): + """Set the voice to speak with + (such as "dfki-prudence-hsmm").""" + self.voice = a_voice + + def get_voice(self): + """Get the voice to speak with + (such as "dfki-prudence-hsmm").""" + self.voice + + def generate(self, message): + """Given a message in message, + return a response in the appropriate + format.""" + raw_params = {"INPUT_TEXT": message, + "INPUT_TYPE": self.input_type, + "OUTPUT_TYPE": self.output_type, + "LOCALE": self.locale, + "AUDIO": self.audio, + "VOICE": self.voice, + } + params = urllib.urlencode(raw_params) + headers = {} + + # Open connection to self.host, self.port. + conn = httplib.HTTPConnection(self.host, self.port) + + # conn.set_debuglevel(5) + + conn.request("POST", "/process", params, headers) + response = conn.getresponse() + if response.status != 200: + print response.getheaders() + raise RuntimeError("{0}: {1}".format(response.status, + response.reason)) + return response.read() + +# If this is invoked as a program, just give +# a greeting to show it is working. +# The platform specific code is moved to this +# part so that this file may be imported without +# bringing platform specific code in. +if __name__ == "__main__": + + # For handling command line arguments: + import sys + import platform + + # check we are on Windows: + system = platform.system().lower() + if (system == "windows"): + + import winsound + + class Player: + def __init__(self): + pass + + def play(self, a_sound): + winsound.PlaySound(a_sound, winsound.SND_MEMORY) + + #if ("cygwin" in system): + else: + # Not sure how to do audio on cygwin, + # portably for python. So have a sound + # player class that doesn't play sounds. + # A null object, if you like. + class Player: + def __init__(self): + pass + + def play(self, a_sound): + print("Here I would play a sound if I knew how") + pass + + # Probably want to parse arguments to + # set the voice, etc., here + + client = maryclient() + client.set_audio("WAVE_FILE") # for example + + player = Player() + the_sound = client.generate("hello from Mary Text to Speech, with Python.") + if client.output_type == "AUDIO": + player.play(the_sound) + +# vi:set sw=4 et: diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient.cgi b/external/marytts-5.1.2/doc/examples/client/maryclient.cgi new file mode 100644 index 00000000..876a846e --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient.cgi @@ -0,0 +1,177 @@ +#!/usr/bin/perl -T +# -*- Mode: Perl -*- +# MARY Text-to-Speech System +# CGI Script implementing a simple mary client, +# can be used for web pages. +########################################################################## +# Copyright (C) 2000-2006 DFKI GmbH. +# All rights reserved. Use is subject to license terms. +# +# Permission is hereby granted, free of charge, to use and distribute +# this software and its documentation without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of this work, and to +# permit persons to whom this work is furnished to do so, subject to +# the following conditions: +# +# 1. The code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Any modifications must be clearly marked as such. +# 3. Original authors' names are not deleted. +# 4. The authors' names are not used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE +# CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +# THIS SOFTWARE. +########################################################################## +# Author: Marc Schroeder + +use strict; +use IO::Socket; +use CGI; + +# variables getting their values from form: +my ($inputtext, $in, $out, $audiotype, $voice); + +# little helpers: +my ($var, $tmp); + +# contacting the mary server: +my ($host, $port, $maryInfoSocket, $maryDataSocket, $id); + +# helping with audio output: +my ($save_to_disk, $audiosubtype, $filename); + + +my $cgi = new CGI; +my @param = $cgi->param(); +$inputtext = $cgi->param('inputtext'); +$in = $cgi->param('in'); +$out = $cgi->param('out'); +$audiotype = $cgi->param('audiotype'); +$save_to_disk = $cgi->param('save_to_disk'); +$voice = $cgi->param('voice'); + +my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); +$year += 1900; +printf STDERR "[%04i-%02i-%02i %02i:%02i:%02i] ", $year, $mon, $mday, $hour, $min, $sec; +print STDERR "Request from ",$cgi->remote_user(),"@",$cgi->remote_host(),": \n"; +print STDERR " in=",$in; +print STDERR " out=",$out; +print STDERR " audiotype=",$audiotype; +print STDERR " voice=",$voice; +print STDERR " save_to_disk=",$save_to_disk,"\n"; +print STDERR " inputtext: "; +print STDERR $inputtext,"\n"; + + +# Limit inputtext length to 5000 bytes: +if (length $inputtext > 5000) { + $inputtext = substr $inputtext, 0, 5000; +} + + +# set audio subtype +if ($out eq "AUDIO") { + if ($audiotype eq "AU") { + $audiosubtype = "basic"; + $filename = "mary.au"; + } elsif ($audiotype eq "AIFF") { + $audiosubtype = "x-aiff"; + $filename = "mary.aiff"; + } elsif ($audiotype eq "WAVE") { + $audiosubtype = "x-wav"; + $filename = "mary.wav"; + } elsif ($audiotype eq "MP3") { + $audiosubtype = "mp3"; + $filename = "mary.mp3"; + } else { + $audiosubtype = "x-wav"; + $filename = "mary.wav"; + } +} + +# announce data type on stdout +if ($save_to_disk) { + print "Content-Type: application/octet-stream"; +} else { + print "Content-Type: audio/$audiosubtype"; +} +print "\nContent-Disposition: filename=\"$filename\"\n\n"; + +# contact mary server +$host = "cling.dfki.uni-sb.de"; +$port = 59125; + +# create a tcp connection to the specified host and port +$maryInfoSocket = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port) + or die "can't connect to port $port on $host: $!"; + +# avoid buffering when writing to server: +$maryInfoSocket->autoflush(1); # so output gets there right away + +########## Write input to server: ########## +# formulate the request: +print $maryInfoSocket "MARY IN=$in OUT=$out AUDIO=$audiotype"; +if ($voice && $voice ne 'v') { print $maryInfoSocket " VOICE=$voice"; } +print $maryInfoSocket " LOG=\"REMOTE_HOST=$ENV{'REMOTE_HOST'}", + ", REMOTE_ADDR=$ENV{'REMOTE_ADDR'}\""; +print $maryInfoSocket "\015\012"; + +# receive a request ID: +$id = <$maryInfoSocket>; + +# open second socket for the data: +$maryDataSocket = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port) + or die "can't connect to port $port on $host: $!"; +# identify with request number: +print $maryDataSocket $id; # $id contains a newline character + +# copy $inputtext to mary data socket +print $maryDataSocket $inputtext; + +# mark end-of-request: +print $maryDataSocket "\015\012"; # that is a \n, actually +$maryDataSocket->shutdown(1); # we have stopped writing data + +########## Read output from server: ########## +# copy the data socket to standard output +if ($out ne "AUDIO") { # text output + my $line; + while (defined ($line = <$maryDataSocket>)) { + print STDOUT $line; + } +} else { # audio data output + my $nr; # number of bytes read + my $buf; # buffer to read into + my $outnr; # number of bytes written + while($nr = read($maryDataSocket, $buf, 8192)) { + # (read returns no. of bytes read, 0 at eof) + print STDOUT $buf + or die "Write error on stdout"; + } # while read something from socket +} # audio output + +### Read complaints from server: +my $line; +while (defined ($line = <$maryInfoSocket>)) { + print STDERR $line; +} + + + + + + + diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient.pl b/external/marytts-5.1.2/doc/examples/client/maryclient.pl new file mode 100644 index 00000000..8b3f5f80 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient.pl @@ -0,0 +1,136 @@ +#!/usr/bin/env perl +# +# MARY Text-to-Speech System +# Minimal Socket client (for demonstration) +########################################################################## +# Copyright (C) 2000-2006 DFKI GmbH. +# All rights reserved. Use is subject to license terms. +# +# Permission is hereby granted, free of charge, to use and distribute +# this software and its documentation without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of this work, and to +# permit persons to whom this work is furnished to do so, subject to +# the following conditions: +# +# 1. The code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Any modifications must be clearly marked as such. +# 3. Original authors' names are not deleted. +# 4. The authors' names are not used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE +# CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +# THIS SOFTWARE. +########################################################################## +# Author: Marc Schroeder +# This is a minimal version of a socket client for the mary TtS system. +# It is intended to be used as a model for writing socket clients for +# particular applications. All input verification, command line options, +# and other luxury have been omitted. +# +# Usage: +# maryclient.pl infile.txt > outfile.wav +# +# Input/output formats and other options must be set in the perl code directly. +# See also Protocol.html for a description of the Protocol. +# + +use strict; +use IO::Socket; + +############################ +# Package-global variables # +############################ +# global settings: +my $maryInfoSocket; # handle to socket server +my $maryDataSocket; # handle to socket server +my $host; # string containing host address +my $port; # socket port on which we listen +my ($in, $out, $audiotype); # requested input / output format +my $voice; # default voice +my $id; # request ID + +###################################################################### +################################ main ################################ +###################################################################### + +STDOUT->autoflush(1); + +$host = "cling.dfki.uni-sb.de"; +$port = 59125; +$in = "TEXT_DE"; +$out = "AUDIO"; +$audiotype = "MP3"; +#$audiotype = "WAVE"; +#$voice = "male"; +$voice = "de3"; + +# create a tcp connection to the specified host and port +$maryInfoSocket = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port) + or die "can't connect to port $port on $host: $!"; + +# avoid buffering when writing to server: +$maryInfoSocket->autoflush(1); # so output gets there right away + +########## Write input to server: ########## +# formulate the request: +print $maryInfoSocket "MARY IN=$in OUT=$out AUDIO=$audiotype"; +if ($voice) { print $maryInfoSocket " VOICE=$voice"; } +print $maryInfoSocket "\015\012"; + +# receive a request ID: +$id = <$maryInfoSocket>; +chomp $id; chomp $id; + +# open second socket for the data: +$maryDataSocket = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port) + or die "can't connect to port $port on $host: $!"; +# identify with request number: +print $maryDataSocket $id, "\015\012"; + +# copy standard input and/or files given on the command line to the socket +while (defined (my $line = <>)) { + print $maryDataSocket $line; +} +# mark end-of-request: +print $maryDataSocket "\015\012"; # that is a \n, actually +shutdown($maryDataSocket, 1); # we have stopped writing data + +########## Read output from server: ########## +# copy the data socket to standard output +if ($out ne "AUDIO") { # text output + my $line; + while (defined ($line = <$maryDataSocket>)) { + print STDOUT $line; + } +} else { # audio data output + my $nr; # number of bytes read + my $buf; # buffer to read into + my $outnr; # number of bytes written + while($nr = read($maryDataSocket, $buf, 100000)) { + # (read returns no. of bytes read, 0 at eof) + print STDOUT $buf + or die "Write error on stdout"; + } # while read something from socket +} # audio output + +### Read complaints from server: +my $line; +while (defined ($line = <$maryInfoSocket>)) { + print STDERR $line; +} + + + diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient.rb b/external/marytts-5.1.2/doc/examples/client/maryclient.rb new file mode 100644 index 00000000..c4156cbb --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient.rb @@ -0,0 +1,261 @@ +#!/usr/bin/env ruby +# +# A basic mary client in Ruby, +# kindly donated to the MARY TTS project +# by Hugh Sasse. Thanks Hugh! + + +# Ruby client for the MARY TTS HTTP server. +# This is for Windows only, and relies on +# the Win32-Sound gem to access the audio. +# +# + +require 'rubygems' +require 'net/http' +require 'uri' + +# A fairly minimal client class for the +# MARY TTS system. This uses the modern +# HTTP interface to access the server. +# At present, this doesn't wrap the methods +# which provide documentation or lists of +# voices or features. +class MaryClient + attr_accessor :host, :port + attr_accessor :input_type, :output_type + attr_accessor :locale, :audio, :voice + + # Set up the defaults for the MARY TTS + # server, which is assumed to be running + # on the local host, with British voices + # installed. These may be modified with + # the appropriate methods. + # host = 127.0.0.1) + # port = 59125 + # input_type = "TEXT" + # output_type = "AUDIO" + # audio = "WAVE_FILE" + # locale = "en_GB" + # voice = "dfki-prudence-hsmm" + def initialize + @host = "127.0.0.1" # The local machine + @port = 59125 + @input_type = "TEXT" + @output_type = "AUDIO" + @locale = "en_GB" + @audio = "WAVE_FILE" + @voice = "dfki-prudence-hsmm" + end + + # Process a text message, which with a + # new client, will return the audio. + # This is so that platform dependent parts + # are kept separate. + def generate(message) + raw_params = {"INPUT_TEXT" => message, + "INPUT_TYPE" => @input_type, + "OUTPUT_TYPE" => @output_type, + "LOCALE" => @locale, + "AUDIO" => @audio, + "VOICE" => @voice, + } + res = Net::HTTP.post_form(URI.parse("http://#{@host}:#{@port}/process"), raw_params) + res.value # Throw an exception on failure + #puts res.body + return res.body + end +end + + +# If this invoked as a program with no +# argumens, just give a greeting to show +# that it is working. If arguments are +# supplied, process options to work out +# what to do with the arguments. +if __FILE__ == $0 + + # These files are only loaded when this is + # invoked as a program. + require 'rbconfig' + require 'getoptlong' + + # PLATFORM SPECIFIC CODE. + # Needs more work [!] + case Config::CONFIG['host_os'] + when /darwin/i + raise NotImplementedError.new("Don't know how to play audio on a Mac") + when /linux/i + raise NotImplementedError.new("Far too many ways to play audio on Linux, you'll need to choose something") + when /sunos|solaris/i + raise NotImplementedError.new("Have not played audio on Suns for too long to implement this.") + when /java/i + raise NotImplementedError.new("Don't know how to play audio from Java ") + when /win32|cygwin|mingw32/i + # The various things that can use the Win32 + # sound gem + require 'win32/sound' + # Create a player class that will play the + # sound that the Mary TTS system returns + class Player + + # Play the audio passed in. + # Possibly this should receive the audio + # type so we can check that we can play it, + # but at the moment that is the + # responsibility of the user. + def self.play(sound) + Win32::Sound.play(sound, Win32::Sound::MEMORY) + end + end + else + raise NotImplementedError.new("Haven't thought how to support this OS yet") + end + + + client = nil + split = "" + + if ARGV.size.zero? + client = MaryClient.new() + sound = client.generate("Hello from Mary Text to Speech with Ruby.") + Player.play(sound) + else + args_mode = :words + stdout_mode = :absorb + opts = GetoptLong::new( + ["--audio", "-a", GetoptLong::REQUIRED_ARGUMENT], + ["--echo", "-e", GetoptLong::NO_ARGUMENT], + ["--help", "-h", GetoptLong::NO_ARGUMENT], + ["--host", "-H", GetoptLong::REQUIRED_ARGUMENT], + ["--input-type", "-i", GetoptLong::REQUIRED_ARGUMENT], + ["--locale", "-l", GetoptLong::REQUIRED_ARGUMENT], + ["--read", "-r", GetoptLong::NO_ARGUMENT], + + ["--split", "-s", GetoptLong::REQUIRED_ARGUMENT], + ["--output-type", "-o", GetoptLong::REQUIRED_ARGUMENT], + ["--port", "-P", GetoptLong::REQUIRED_ARGUMENT], + ["--tee", "-t", GetoptLong::NO_ARGUMENT], + ["--voice", "-v", GetoptLong::REQUIRED_ARGUMENT] + ) + + opts.each do |opt, arg| + unless ["--help", "-h"].include?(opt) + # skip if we are only getting help + client ||= MaryClient.new() + end + case opt + when "--help", "-h" + puts <<-EOHELP +Usage: #{$0} [options] [arguments] +--audio -a + Audio format. Defualt: WAVE_FILE +--echo -e + Act as an echo command and send output + arguments to the synthesizer only (not + to standard output. + Turns off --read|-r +--help -h + Print this help, then exit. +--host -H + The host which is the server. + Default: 127.0.0.1 +--input-type -i + The type of the input supplied to the + TTS system. Default: TEXT +--locale -l + The locale of the input. Default: en_GB +--output-type -o + The output type from the TTS system. + Default: AUDIO +--port -P + The port for the TTS server + Default: 59125 +--read -r + Read the files passed as arguments. + Turns off --echo|-e +--split -s (lines|paragraphs) + When reading files, split the input + into lines or paragraphs. Paragraphs + mean reading up to the next double + newline. Note, the argument is literally + "lines" or "paragraphs" (or some + abbreviation of those) without the + quotes. + Default is paragraphs. +--tee -t + Act as tee: send the output to the TTS + system, and to standard output. +--voice -v + The voice to use. + Default: dfki-prudence-hsmm + EOHELP + exit(0) + when "--audio", "-a" + client.audio = arg + when "--echo", "-e" + args_mode = :words + when "--host", "-H" + client.host = arg + when "--input-type", "-i" + client.input_type = arg + when "--locale", "-l" + client.locale = arg + when "--output-type", "-o" + client.output_type = arg + when "--port", "-P" + client.port = arg.to_i + when "--read", "-r" + args_mode = :files + when "--split", "-s" + case arg + when /^p/i + split = "" + when /^l/i + split = $/ + end + when "--tee", "-t" + stdout_mode = :emit + when "--voice", "-v" + client.voice = arg + end + end + + client ||= MaryClient.new() + case args_mode + when :words + input_text = ARGV.join(" ") + unless input_text =~ /\A\s*\Z/m + sound = client.generate(input_text) + if client.output_type == "AUDIO" + Player.play(sound) + end + end + if stdout_mode == :emit + puts input_text + end + when :files + # Slurp in paragraphs so sentences + # don't get broken in stupid places. + $/ = split # paragraph mode + ARGF.each do |paragraph| + begin + unless paragraph =~ /\A\s*\Z/m + sound = client.generate(paragraph) + if client.output_type == "AUDIO" + # and client.audio == "WAVE_FILE" + Player.play(sound) + end + end + rescue Exception => e + puts "got error #{e} while trying to say #{paragraph.inspect}" + raise + end + if stdout_mode == :emit + puts paragraph + end # end if + end # end ARGF.each + end # end case + end # if ARGV.size.zero? +end + diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient.tcl b/external/marytts-5.1.2/doc/examples/client/maryclient.tcl new file mode 100644 index 00000000..3a358235 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient.tcl @@ -0,0 +1,705 @@ +# Tcl/Tk MARY TTS client. + +# This has been tested on Windows, and because +# of the use of sound there will be portability +# issues. However, there should be enough here +# for a reasonable start at a client, for any +# platform that supports Tcl/Tk. The platform +# specific code has, as far as possible, been +# isolated in the part of the code that detects +# whether this is being run as a program. + +# Notes: +# More work will need to be done with this, +# in order to make the code clean. It should +# probably be wrapped in a package, to solve +# any namespace issues. There are a lot of +# global variables. It seems that some of +# these are necessary for the menus to work. +# Handling of temporary files could be improved. + +# TODO: +# Create modifier sliders, for the effects. +# Extend the query proc to make use of them. +# Turn the Help menu into something more useful. +# Debug the actions for the Edit menu. +# Provide a means of getting example inputs +# from the server. +# Provide a means of re-loading all the +# dynamically collected information when the +# server is changed from the menu. This means +# that we need to delete the existing menu +# entries in order to add them correctly. +# How do we ensure temporary files are removed +# in the event of a problem? if {catch {}} ...? +# Maybe leaving them around is diagnostic info? +# Make that an option? +# Add error handling code for network and disk +# failures likely to beset such clients. +# Add sensible defaults for things the user must +# always set at startup, but these will be +# platform spacific. Always default to Audio +# output for example, or is it possible that +# people have no voices installed? + + +# This is a GUI, so: +package require Tk + +# We are communicating with the Mary server +# with HTTP. +package require http + +# Use the local machine in preference to the +# one in Germany. +set mary_tts_default_host "127.0.0.1" +set mary_tts_default_port 59125 + +# Actual host and port, and global old +# copies to allow revert on cancel in the +# dialogues. Apparently upvar #0 is the +# norm for that sort of thing [Tcl Wiki] +set mary_tts_host $mary_tts_default_host +set old_mary_tts_host $mary_tts_host +set mary_tts_port $mary_tts_default_port +set old_mary_tts_port $mary_tts_port + +# Informational URLs +set informational_urls [ list \ +version datatypes voices \ +audioformats audioeffects ] + +####### + +# Obtain a static page from the server, i.e. +# no parameters are needed to get it. +proc get_page { relative_url } { + global mary_tts_host mary_tts_port + set url http://$mary_tts_host:$mary_tts_port/$relative_url + set result [::http::geturl $url] + return [::http::data $result] +} + +proc list_of_lines {str} { + return [ split $str "\n" ] +} + + +# We will need to collect this information +# when we have the server and port chosen. +proc get_audioeffects {} { + return [list_of_lines [get_page audioeffects] ] +} + +proc get_audioformats {} { + return [list_of_lines [get_page audioformats] ] +} + +proc get_datatypes {} { + return [ list_of_lines [get_page datatypes] ] +} + + +proc get_voices {} { + return [list_of_lines [get_page voices] ] +} + +# Handling post queries. + +# Submit the query to the server, using the +# http POST method. +proc make_query {url encoded_params} { + set http [::http::geturl $url -query $encoded_params] + set result [::http::data $http] + return $result +} + +# Get the text from the input text area +proc get_input_text {} { + return [.io.inp.input_area get 1.0 end] +} + +# Get the text from the output text area +proc get_output_text {} { + return [.io.out.output_area get 1.0 end] +} + +# Collect the audio data from the server. +proc collect_audio_data {text_to_process} { + global mary_tts_host mary_tts_port + global inputtype outputtype locales + global audioformat voice + set url "http://$mary_tts_host:$mary_tts_port/process" + # ::http::formatQuery converts a list of + # key value pairs into the correct format + # for http POST. + set params [::http::formatQuery INPUT_TEXT $text_to_process INPUT_TYPE $inputtype OUTPUT_TYPE $outputtype LOCALE $locales($voice) AUDIO $audioformat VOICE $voice ] + set result [make_query $url $params] + return $result +} + +# Pushes the query to the server and gets +# the results back, displaying or playing +# them. +proc generate_output {text_to_process} { + global outputtype + set result [collect_audio_data $text_to_process] + if {$outputtype eq "AUDIO"} { + # call the platform dependent implementation. + play $result + } else { + clear_output + add_message $result + } + # Return the result so we can save it if + # the user requires it. + return $result +} + + +# These next procs are for handling the +# lists of data one gets back from the server +# which possibly have several words per line, +# separated by spaces. + +# If the first word of each listed line is +# significant, extract the list of first words. +proc collect_first_words_of_phrase_list {a_list} { + for {set i 0} {$i < [llength $a_list]} {incr i} { + set data [lindex $a_list $i ] + set word [ lindex [split $data " "] 0 ] + lappend words $word + } + return $words +} + + +# If the second word of each listed line is +# significant, extract the list of second words. +proc collect_second_words_of_phrase_list {a_list} { + for {set i 0} {$i < [llength $a_list]} {incr i} { + set data [lindex $a_list $i ] + set word [ lindex [split $data " "] 1 ] + lappend words $word + } + return $words +} + + +# The list of datatypes must be separated into +# input data types and output data types so that +# interactions with the server make sense. +# This handles the inputs. +proc collect_first_words_of_input_types {a_list} { + for {set i 0} {$i < [llength $a_list]} {incr i} { + set data [lindex $a_list $i ] + if {[ string match -nocase "*input*" $data ]} { + set word [ lindex [split $data " "] 0 ] + lappend words $word + } + } + return $words +} + + +# The list of datatypes must be separated into +# input data types and output data types so that +# interactions with the server make sense. +# This handles the outputs. +proc collect_first_words_of_output_types {a_list} { + for {set i 0} {$i < [llength $a_list]} {incr i} { + set data [lindex $a_list $i ] + if {[string match -nocase "*output*" $data]} { + set word [ lindex [split $data " "] 0 ] + lappend words $word + } + } + return $words +} + +# setup all the variables to hold voices, +# audio options, etc., based on what the +# server can do. +proc setup_globals {} { + global audioeffects audioformats voices + global inputtypes outputtypes audioformat voice + global inputtype outputtype locales + + set audioeffects [get_audioeffects] + set audioformats [get_audioformats] + set audioformat [lindex $audioformats 0 ] + set datatypes_data [get_datatypes] + set inputtypes [collect_first_words_of_input_types $datatypes_data] + set inputtype [lindex $inputtypes 0] + set outputtypes [collect_first_words_of_output_types $datatypes_data] + set outputtype [lindex $outputtypes 0] + set voices_data [get_voices] + set voices [collect_first_words_of_phrase_list $voices_data] + set locales_list [collect_second_words_of_phrase_list $voices_data ] + for {set i 0} {$i < [llength $voices]} {incr i} { + set locales([lindex $voices $i]) [lindex $locales_list $i] + } + set voice [lindex $voices 0] +} + +# A general procedure for filling in the +# elements of a listbox from a list. +# At present this is unused, but it could +# be useful later. [It took a while to +# figure out so I'm not ready to kill it +# with YAGNI.] +proc add_listbox_items {a_var a_widget} { + upvar $a_var var + foreach item $var { + $a_widget insert end $item + } +} + +# Create the menubuttons along the top. +# Usual File, Edit and Help menus plus +# those to set attributes. +proc create_menubuttons {} { + set buttons [ list file File edit Edit \ + server "Server" \ + inputtype "Input type" outputtype "Output type" \ + voice Voice \ + audioformat "Audio format" \ + textstyle "Text style" help Help ] + + set count 1 + foreach { menu_tag string_tag} $buttons { + menubutton .menus.$menu_tag -text $string_tag \ + -menu .menus.${menu_tag}.menu -underline 0 -font ClientFont + menu .menus.${menu_tag}.menu -tearoff true + grid .menus.$menu_tag -in .menus -row 1 -column $count -sticky w + incr count + } +} + +# Get the contents of a text file for reading +# or loading into a text widget, etc. +proc text_file_contents {what_for} { + set a_file [tk_getOpenFile -title $what_for ] + set the_text "" + + if {$a_file != ""} { + set a_stream [open $a_file r ] + set the_text [read $a_stream] + close $a_stream + } + + return $the_text +} + + +# Save the_text to a text file specified +# by the user, for the given reason (what_for). +# At the moment there is no error handling +# for this (disk full, write protected, etc). +proc save_text_file {the_text what_for} { + set a_file [tk_getSaveFile -title $what_for -parent .] + if {$a_file != ""} { + set a_stream [open $a_file w ] + puts $a_stream $the_text + close $a_stream + } +} + +# Save the_data to a binary file specified +# by the user, for the given reason (what_for), +# a text string. +# At the moment there is no error handling +# for this (disk full, write protected, etc). +proc save_binary_file {the_data what_for} { + set a_file [tk_getSaveFile -title $what_for -parent .] + if {$a_file != ""} { + set a_stream [open $a_file w ] + fconfigure $a_stream -translation binary + puts -nonewline $a_stream $the_data + close $a_stream + } +} + +# Create the menu for File operations +proc create_menu_file {} { + set fmenu .menus.file.menu + $fmenu add command -label "New" \ + -font ClientFont -command { + .io.inp.input_area delete 1.0 end + } + # Replace the contents of the input text + # widget by the data from the open file. + # YAGNI, but is there any reason + # to allow inserting a file, rather than + # replacing the text with file contents? + # + $fmenu add command -label "Open" \ + -font ClientFont -command { + set the_text [text_file_contents "File to load"] + if {$the_text != ""} { + .io.inp.input_area delete 1.0 end + .io.inp.input_area insert end $the_text + } + } + + $fmenu add command -label "Read" \ + -font ClientFont -command { + generate_output [text_file_contents "File to read"] + } + # How to make these disabled for now? + $fmenu add command -label "Save Input" \ + -font ClientFont -command { + set the_text [get_input_text] + save_text_file $the_text "Save Input" + } + $fmenu add command -label "Save Output" \ + -font ClientFont -command { + set the_text [get_output_text] + save_text_file $the_text "Save Output" + } +} + +# Create the menu for edit operations +proc create_menu_edit {} { + set emenu .menus.edit.menu + $emenu add command -label "Select All from Input Area" \ + -font ClientFont -command { + # This code says copy the selection as well. + # May be wrong for some platforms, but is + # it more useful? + .io.inp.input_area tag add sel 1.0 end + event generate .io.inp.input_area <> +} + $emenu add command -label "Select All from Output Area" \ + -font ClientFont -command { + # This code says copy the selection as well. + # May be wrong for some platforms, but is + # it more useful? + .io.out.output_area tag add sel 1.0 end + event generate .io.out.output_area <> +} + $emenu add command -label "Copy from Input Area" \ + -font ClientFont -command { + # this appears not to work. FIXME + event generate .io.inp.input_area <> + } + $emenu add command -label "Copy from Output Area" \ + -font ClientFont -command { + # this appears not to work. FIXME + event generate .io.out.output_area <> + } + $emenu add command -label "Paste into Input Area" \ + -font ClientFont -command { + # this appears not to work. FIXME + event generate .io.inp.input_area <> + } + $emenu add command \ + -font ClientFont -label "Insert example text into Input Area"\ + -command { + } + # Add specific editing commands here later. + # For example, we would like to be able to + # add whole tags to the XML based formats, + # wrap matching tags around selected text. + # Also we need to find out what happens with + # copy cut and paste, given that X Windows + # is different from MS Windows. + # Allow example text to be inserted. + # However, my thinking is that this should not + # overwrite as it is in the Java application, + # because this rubs out edits when switching + # voices, and this can be annoying when + # exploring the system. +} + +# Set the server properties, mostly just +# host and port. Maybe later protocol will +# be possible for https connections? +proc create_menu_server {} { + set smenu .menus.server.menu + $smenu add command -label "host" -font ClientFont -command { + create_entry_dialog "MARY TTS server name" "hostname/IP Address" mary_tts_host + } + $smenu add command -label "port" -font ClientFont -command { + create_entry_dialog "MARY TTS server port" "pott number" mary_tts_port + } +} + +# setup the fonts for the various areas on the dipslay. +proc setup_font {family size} { + foreach win {.io .controls .entry.dialogue } { + font configure ClientFont -family $family -size $size + } +} + +# Create the menu for changing the text size. +proc create_menu_textstyle {} { + set tmenu .menus.textstyle.menu + + $tmenu add cascade -label "Courier" -underline 0 -menu \ + $tmenu.courier -font ClientFont + $tmenu add cascade -label "Times" -underline 0 -menu \ + $tmenu.times -font ClientFont + $tmenu add cascade -label "Helvetica" -underline 0 -menu \ + $tmenu.helvetica -font ClientFont + foreach {name family} [list $tmenu.courier Courier \ + $tmenu.times Times $tmenu.helvetica Helvetica ] { + set m1 [menu $name] + foreach pts {6 7 8 9 10 12 14 16 18 20 24 28 32 36} { + $m1 add command -label "$pts" -font ClientFont\ + -command [list setup_font $family $pts ] + } + } +} + + + +# Create the menu for Help +proc create_menu_help {} { + # This is all pretty much "wet paint" + # Is there enough to merit separate menus? + set hmenu .menus.help.menu + $hmenu add command -label "Introduction" -font ClientFont\ + -command { + tk_messageBox -message "This is a basic Tcl/Tk +client for the MARY TTS system. Most of the options +are reached through the menus on the top. Some +facilities are presently lacking. + +Most of the interface should be self-explanatory. +In the File menu, Read will read a given file aloud +(or at least take it as input for the present +form of processing), whereas Open will load it +into the input area. Save input and Save output +refer to the contents of the text windows. The +save button next to the play button will save +the output to a file; this is assumed to be a +text file, unless the output is audio, in which +case it is a binary file. + +The Edit menu has cut and paste facilities, +but these don't seem to work reliably. The +default key bindings for text areas should +be useable. + +You will need to set the input and output types +and the audio format before pressing play. +Code does not yet exist to figure out sensible +defaults for your platform. + +This does not have support for the effects, yet. + +Contributions from developers welcome." -type ok + } + $hmenu add command -label "About" -command {} -font ClientFont +} + +# We need to create menus for the available +# voices and audio formats, etc. +# When we have the data for these menus from +# the server, create them by using the global +# lists of information. +proc create_radio_menu_from_list {what} { + global $what + set plural "${what}s" + upvar 1 $plural var + foreach item $var { + .menus.${what}.menu add radiobutton -label $item -variable $what \ + -value $item -font ClientFont + } +} + +proc reset_entry_and_var {a_variable} { + upvar #0 $a_variable var + upvar #0 old_$a_variable old_var + set var $old_var + destroy .entry_dialogue +} +# Create the toplevel for choosing a host +# or port, something taken from an entry. +proc create_entry_dialog {a_message a_label a_variable} { + upvar #0 $a_variable var + upvar #0 old_$a_variable old_var + toplevel .entry_dialogue + label .entry_dialogue.the_message -text $a_message \ + -font ClientFont + label .entry_dialogue.the_label -text $a_label -font ClientFont + entry .entry_dialogue.the_entry -textvariable $a_variable \ + -font ClientFont + button .entry_dialogue.ok -text "OK" -font ClientFont -command { + destroy .entry_dialogue + } + button .entry_dialogue.cancel -text "Cancel" -font ClientFont \ + -command "reset_entry_and_var $a_variable" + + grid .entry_dialogue.the_message -row 1 -column 1 + grid .entry_dialogue.the_label -row 2 -column 1 + grid .entry_dialogue.the_entry -row 2 -column 2 + grid .entry_dialogue.ok -row 3 -column 1 + grid .entry_dialogue.cancel -row 3 -column 2 +} + +# Add a message to the end of the output +# text widget. +proc add_message {a_message} { + .io.out.output_area configure -state normal + .io.out.output_area insert end $a_message + .io.out.output_area configure -state disabled +} + + +# Clear the text in the output text widget. +proc clear_output {} { + .io.out.output_area configure -state normal + .io.out.output_area delete 1.0 end + .io.out.output_area configure -state disabled +} + +# Sound generation is platform dependent. +# This provides an "abstract" function to +# be overridden by the platform dependent +# code. In this case it alerts the user +# in the output window that nothing is going +# to happen. +proc play {sound} { + add_message \ + "play sound not implemented on this platform apparently" +} + +# Graphical stuff. + +# In order to be able to scale the font, define a font. +font create ClientFont -family [font actual TkDefaultFont -family] \ + -size [font actual TkDefaultFont -size] + +frame .menus +create_menubuttons +create_menu_file +create_menu_edit +create_menu_server +create_menu_textstyle +create_menu_help +# Fill in the other menus at runtime. + +# .io communicates text with the user, +# through an input and output window. +frame .io +frame .io.inp +frame .io.out +# .controls will hold the play button and +# the effects controls. +frame .controls + +# Draw the controls in .io +label .io.inp.input_label -text "Input Area" -font ClientFont +text .io.inp.input_area -height 10 -width 40 \ +-xscrollcommand ".io.inp.input_x set" \ +-yscrollcommand ".io.inp.input_y set" -font ClientFont +scrollbar .io.inp.input_x -orient horizontal \ +-command ".io.inp.input_area xview" +scrollbar .io.inp.input_y -orient vertical \ +-command ".io.inp.input_area yview" + +label .io.out.output_label -text "Output Area" -font ClientFont +text .io.out.output_area -height 10 -width 40 -state disabled \ +-xscrollcommand ".io.out.output_x set" \ +-yscrollcommand ".io.out.output_y set" -font ClientFont +scrollbar .io.out.output_x -orient horizontal \ +-command ".io.out.output_area xview" +scrollbar .io.out.output_y -orient vertical \ +-command ".io.out.output_area yview" + +grid .io.inp -in .io -row 1 -column 1 +grid .io.out -in .io -row 1 -column 2 +grid .io.inp.input_label -in .io.inp -row 1 -column 1 +grid .io.inp.input_area -in .io.inp -row 2 -column 1 +grid .io.inp.input_y -in .io.inp -row 2 -column 2 -sticky ns +grid .io.inp.input_x -in .io.inp -row 3 -column 1 -sticky ew + +grid .io.out.output_label -in .io.out -row 1 -column 1 +grid .io.out.output_area -in .io.out -row 2 -column 1 +grid .io.out.output_y -in .io.out -row 2 -column 2 -sticky ns +grid .io.out.output_x -in .io.out -row 3 -column 1 -sticky ew + +button .controls.play -text "play" -font ClientFont -command { + generate_output [get_input_text] +} +grid .controls.play -in .controls -row 1 -column 1 + +button .controls.save -text "save" -font ClientFont -command { + global outputtype + set input_text [get_input_text] + if { $outputtype eq "AUDIO" } { + save_binary_file [collect_audio_data $input_text ] "Save audio file" + } else { + save_text_file [collect_audio_data $input_text ] "Save output to file" + } +} + +grid .controls.save -in .controls -row 1 -column 2 + +pack .menus .io .controls -in . -side top + + + +# Detect whether this is the main program +# This test was taken from the Tcl Wiki, and +# seems to work OK. + +if {[info exists argv0] && [file tail [info script]] eq [file tail $argv0]} { + + # Try to find the temporary files directory. + catch { set tmpdir "/tmp" } + catch { set tmpdir $::env(TRASH_FOLDER) } + catch { set tmpdir $::env(TMP) } + catch { set tmpdir $::env(TEMP) } + # This needs better handling of + # possible alternatives + # This is needed for Windows sound only. + + # Do the platform dependent things. + if {$tcl_platform(platform) eq "windows"} { + package require twapi + + proc play {sound} { + global tmpdir + # Write sound to a temporary file + set sndfile [file join $tmpdir "MARYTTS_sound.[pid].wav" ] + set stream [open $sndfile w] + # Make sure the file is binary: + fconfigure $stream -translation binary + puts -nonewline $stream $sound + close $stream + # Play the file. + ::twapi::play_sound $sndfile + # Remove the file. + file delete $sndfile + } + } + # Put other platforms here. + + # Setup the globals with reference to the + # server, which is assumed to be working. + # Since we have options to alter this with + # menu items, there probably needs to be + # some way to reload all this. But we need + # to know how to delete the existing menu + # entries to do that. + setup_globals + create_radio_menu_from_list inputtype + create_radio_menu_from_list outputtype + create_radio_menu_from_list voice + create_radio_menu_from_list audioformat + + # Note, at the moment voices holds locales, + # gender, and voice type + + # At the moment this is just diagnostic: + ## add_message [ join $voices "\n" ] + # it tells us we have a basically working + # system and the list of voices has been + # picked up and manipulated correctly. + # So it is commented out now. +} + + diff --git a/external/marytts-5.1.2/doc/examples/client/texttospeechdemo.html b/external/marytts-5.1.2/doc/examples/client/texttospeechdemo.html new file mode 100644 index 00000000..e788cf50 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/texttospeechdemo.html @@ -0,0 +1,81 @@ + + + + + +