diff --git a/.gitignore b/.gitignore index 1df2052..14555f3 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ build/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store +.env \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Facture_Paiement_43.pdf b/Facture_Paiement_43.pdf new file mode 100644 index 0000000..482fcfb Binary files /dev/null and b/Facture_Paiement_43.pdf differ diff --git a/lib/marytts-5.2.1.zip b/lib/marytts-5.2.1.zip new file mode 100644 index 0000000..f5f9436 Binary files /dev/null and b/lib/marytts-5.2.1.zip differ diff --git a/lib/marytts-5.2.1/.gitattributes b/lib/marytts-5.2.1/.gitattributes new file mode 100644 index 0000000..b03bcca --- /dev/null +++ b/lib/marytts-5.2.1/.gitattributes @@ -0,0 +1,10 @@ +# EOL encoding is LF, but native in working copy +* text=auto + +# batch, markdown, properties, text, and config files should be viewable and user-editable +# -- even in Windows Notepad, so CRLF EOL +*.bat text eol=crlf +*.config text eol=crlf +*.md text eol=crlf +*.properties text eol=crlf +*.txt text eol=crlf diff --git a/lib/marytts-5.2.1/.gitignore b/lib/marytts-5.2.1/.gitignore new file mode 100644 index 0000000..6aa8e8b --- /dev/null +++ b/lib/marytts-5.2.1/.gitignore @@ -0,0 +1,18 @@ +# Maven +target/ + +# Gradle +.gradle/ + +# Eclipse +.settings/ +.project +.classpath + +# misc. testing +tmp/ +log/ +test-output/ + +# OSX +.DS_Store diff --git a/lib/marytts-5.2.1/.travis.yml b/lib/marytts-5.2.1/.travis.yml new file mode 100644 index 0000000..06c4503 --- /dev/null +++ b/lib/marytts-5.2.1/.travis.yml @@ -0,0 +1,18 @@ +sudo: false + +branches: + only: + - master + - 6.x + +language: java +install: mvn install -DskipTests=true -P!standard-with-extra-repos +script: mvn verify +jdk: + - openjdk7 + - oraclejdk7 + - oraclejdk8 + +cache: + directories: + - $HOME/.m2 diff --git a/lib/marytts-5.2.1/LICENSE.md b/lib/marytts-5.2.1/LICENSE.md new file mode 100644 index 0000000..d5231df --- /dev/null +++ b/lib/marytts-5.2.1/LICENSE.md @@ -0,0 +1,62 @@ +MaryTTS Software User Agreement +=============================== + +*26 January 2014* + +MaryTTS licensing +----------------- + +MaryTTS 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 +------------------- + +MaryTTS 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 MaryTTS 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. +* **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:** MaryTTS 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, MaryTTS 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 + MaryTTS 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. +* **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/lib/marytts-5.2.1/README.md b/lib/marytts-5.2.1/README.md new file mode 100644 index 0000000..aa08f4b --- /dev/null +++ b/lib/marytts-5.2.1/README.md @@ -0,0 +1,124 @@ +[![Build Status](https://travis-ci.org/marytts/marytts.svg?branch=5.1.x)](https://travis-ci.org/marytts/marytts) + +# MaryTTS + +This is the source code repository for the multilingual open-source MARY text-to-speech platform (MaryTTS). +MaryTTS is a client-server system written in pure Java, so it runs on many platforms. + +**For a downloadable package ready for use, see [the releases page](https://github.com/marytts/marytts/releases).** + +Older documentation can also be found at http://mary.dfki.de and https://mary.opendfki.de. + +This README is part of the the MaryTTS source code repository. +It contains information about compiling and developing the MaryTTS sources. + +The code comes under the Lesser General Public License LGPL version 3 -- see LICENSE.md for details. + + +## Using MaryTTS in your own Java projects + +### Adding the MaryTTS to your dependencies + +The easiest way to use MaryTTS in your own Java projects is to declare a dependency on a relevant MaryTTS artifact: + +- in the `pom.xml` for Maven: +```xml + + + de.dfki.mary + voice-cmu-slt-hsmm + 5.2.1 + + +``` +- in the `build.gradle` for Gradle +```groovy +repositories { + mavenCentral() +} + +dependencies { + compile 'de.dfki.mary:marytts:5.2.1' +} +``` + + +### Synthesizing speech + +Text to wav basic examples are proposed in this repository +- Maven: https://github.com/marytts/marytts-txt2wav/tree/maven +- Gradle: https://github.com/marytts/marytts-txt2wav/tree/gradle + + +## Using MaryTTS for other programming languages + +If you want to use MaryTTS for other programming languages (like python for example), you need to achieve 3 steps + +1. compiling marytts +2. starting the server +3. query synthesis on the server + + +### Compiling MaryTTS on the command line + +MaryTTS v5.x builds with Maven 3.0.x. +If it is not installed on your system, see +http://maven.apache.org/download.html or install it using your favorite package manager. + +Compiling the MARY system itself can be done using + + mvn install + +in the top-level folder. + +This will compile the system, run all unit and integration tests, package the system to the extent possible, and install it in your local maven repository. + + +### Running the freshly built MaryTTS server + +After a successful compile, you should find a ready-to-run unpacked installation of the MaryTTS server system in `target/marytts-`. +Run the server as + + target/marytts-/bin/marytts-server + +Then connect to it with your browser at http://localhost:59125 or using the `marytts-client` in the same folder. + +The runtime system is also available as deployable packages: + + target/marytts-.zip + +Installation is easy: +unpack anywhere, and run the scripts in the `bin/` folder. + + +### Synthesize speech using the server + +Synthesizing speech, using the server, is pretty easy. +You need to generate proper HTTP queries and deal with the associated HTTP responses. +Examples are proposed : +- python 3: https://github.com/marytts/marytts-txt2wav/tree/python +- shell: https://github.com/marytts/marytts-txt2wav/tree/sh + + +## Developing MaryTTS + +### Working on MaryTTS code + +The recommended workflow for making contributions to the MaryTTS source code is to follow the GitHub model: + +1. fork the MaryTTS repository into your own profile on GitHub, by navigating to https://github.com/marytts/marytts and clicking "fork" (of course you need a GitHub account); + +2. use the `git clone`, `commit`, and `push` commands to make modifications on your own marytts repository; + in this process, make sure to `git pull upstream master` regularly to stay in sync with latest developments on the master repo; + +3. when you think a reusable contribution is ready, open a "pull request" on GitHub to allow for easy merging into the master repository. + +Have a look at the [GitHub documentation](http://help.github.com/) for further details. + + +### IDE configuration + +Wiki pages are available to help you to configure your IDE to develop MaryTTS. +The following IDEs have been tested and documented: + +- Eclipse: https://github.com/marytts/marytts/wiki/Eclipse diff --git a/lib/marytts-5.2.1/build.gradle b/lib/marytts-5.2.1/build.gradle new file mode 100644 index 0000000..fe20671 --- /dev/null +++ b/lib/marytts-5.2.1/build.gradle @@ -0,0 +1,4 @@ +allprojects { + version '5.2.1' + buildDir 'target' +} diff --git a/lib/marytts-5.2.1/doc/ErrorHandling.odp b/lib/marytts-5.2.1/doc/ErrorHandling.odp new file mode 100644 index 0000000..980c4d2 Binary files /dev/null and b/lib/marytts-5.2.1/doc/ErrorHandling.odp differ diff --git a/lib/marytts-5.2.1/doc/ErrorHandling.pdf b/lib/marytts-5.2.1/doc/ErrorHandling.pdf new file mode 100644 index 0000000..cbc55e3 Binary files /dev/null and b/lib/marytts-5.2.1/doc/ErrorHandling.pdf differ diff --git a/lib/marytts-5.2.1/doc/FSTFormat.odt b/lib/marytts-5.2.1/doc/FSTFormat.odt new file mode 100644 index 0000000..b83c39c Binary files /dev/null and b/lib/marytts-5.2.1/doc/FSTFormat.odt differ diff --git a/lib/marytts-5.2.1/doc/Getting_started_with_CARTAnalyzer.txt b/lib/marytts-5.2.1/doc/Getting_started_with_CARTAnalyzer.txt new file mode 100644 index 0000000..8c97a6f --- /dev/null +++ b/lib/marytts-5.2.1/doc/Getting_started_with_CARTAnalyzer.txt @@ -0,0 +1,348 @@ +CARTAnalyzer - Getting Started +------------------------------ + +This document was written by Maximilian Kwapil +during an internship at the DFKI language technology lab +during spring 2007 + +1.)What is a CART? + +A CART is the final part of the unit selection process where +everything that has been done before by various algorithms +comes together. + +It stands for +Classification +And +Regression +Tree + +For us, the TREE part is the most important one. + +2.) ... What? + +When you choose any unit selection voice for text input, +the text is of course thoroughly analysed. +Each single, smallest part/sound of the speech that is to be spoken +(a unit) is then selected from the voice recordings +(thus unit selection) by the CART. +The CART itself contains all recorded units in its leaves. +Above the leaves, there are many many decision nodes that +classify (thus ClassificationTree) a given unit by +several (phonetic) criteria like + + - what phoneme does the unit represent + - is it a vowel or a consonant + - is it stressed or accented + - how many syllables does the current word contain + . +So, when the text is actually synthesized, MARY knows for every +unit how it should sound and goes through the CART of the voice, +using the decision nodes to reach all the way down to the most +fitting leaf. + +3.) What is a leaf? What does a leaf contain? + +A leaf contains several units that best fit the desired sound. +For example a leaf could contain 10 units that should sound like +an "a", unstressed, with 3 words until the end of the sentence +(and the sentence is actually a question). + +The CARTBuilder makes sure that no leaf contains too many units, +so usually there are about 50-70 units per leaf average. + +4.) So what, the concept of the CART and its usage seem flawless + and ideal for unit selection purposes !? + +It is, I'm afraid, not that easy. +After all, nothing is perfect and nor are our algorithms and +those that we use. + +In the audio timeline, the units could have been labelled wrong, +(unit) durations could have been miscalculated. +There are many possible causes for errors along the +voice building way. + +One of the consequences of that is that some of the units +that are stored in the leafs of the CART are in a place +where they should not be. + +For example, we do not want an "a" unit in a leaf that is supposed +to represent an "o", regardless of their other (phonetic) properties. +We also would not want any kind of sound where there should be silence, +etc etc. +The differences may be really really small sometimes but in a homogenous +(ideal) leaf, it still matters. + +5.) I suppose now that the CARTAnalyzer can help me with that? + +Well yes, that's what it was designed for. +The CARTAnalyzer was programmed to go through the leafs of the tree of +a unit selection voice and spot and erase those units that do not +belong there (outliers). +We hope that the sound of the voice will improve when MARY doesn't even +have the chance to choose any outliers for the synthesization process. + +6.) How can the CARTAnalyzer help? + +At the moment, there are two things that can be used for +detecting and erasing outliers: + +- Probability values (or float values): + +The FESTVOX Wagon format that we currently use for building our CARTs +assigns a calculated float value to every single unit in each leaf of +the tree. +In the past we just dismissed those values but they seem vital to +detecting outliers. +We guess that the floats measure some kind of "acoustic similarity" +or "acoustic distance". +We use these in the CARTAnalyzer, since we assume that +(acoustic) outliers would be the most distant ones to all the other units. +Thus, those units with an interestingly high float/p-value should be +cut immediately. + +- Energy level (new, under development): + +One of the main goals of this tool is finding sounds where there should +not be any, particularly finding any kind of sound +in a leaf that should represent silence. +For this purpose, energy seems to be the best criterion for the units, +since silence is the absence of energy/sound and the smallest "noise" +would have significantly high energy levels compared to the +absolute zero of silence. +For that, we use our signal processing tools, particularly +the EnergyAnalyser and the DatagramDoubleDataSource. + +7.) I am confused and I do not know how to use this tool. Help! + +No problem. That's why this wonderful "Getting started" guide was written. +Let's go through these few simple steps together, shall we? + +Step 1: + +Make sure all mary files for the voice that you want to work on +have already been created by the voice import tool. +Also, since we want the float values for all units in the leaves, +the cart.mry file must have been built by the new modified CARTBuilder. +(When a search for "ExtendedClassificationTree" succeeds in your + CARTBuilder.java then that is the right version for our purposes.) + +Step 2: + +As soon as you have all your voice files ready, you can start the +program in the main voice directory (i.e. ..../full_voice, ..../arctic_voice etc). +Make sure to include the proper classpath and give it some memory to use. +Here is a sample shell script that you can copy to and execute in your +proper voice directory (replace $MARY_BASE with your openmary directory): + + java -cp $MARY_BASE/java/mary-common.jar:$MARY_BASE/java/log4j-1.2.8.jar:$MARY_BASE/java/jsresources.jar: + $MARY_BASE/java/signalproc.jar + -ea -Xms40m -Xmx1024m $* de.dfki.lt.mary.gizmos.CARTAnalyzer + +Step 3: + +Immediately after execution, the program should output something like + CARTAnalyzer started. + Initializing... +Depending on the size of the voice, several seconds will pass until + CART loaded from ....../xyz_voice/mary_files/cart.mry + [some statistical output as to how many leafs and units there are] + ca:cL:1 ?> + +If you can see all this and you are in the main command line (ca:cL:x ?>): +Congratulations, the CARTAnalyzer has initialized perfectly. + +By default, as you can see, the CARTAnalyzer (ca) loads the cart.mry +in the mary_files directory (the one usually created by CARTBuilder) +automatically. +Also, since we navigate through the leaves of the tree, the index number +of the current leaf is also shown in every command line (cL:x). + +For the next step, you have three options: + +Option a) + +This is the easiest option of them all. Use it if you don't have the time +or just want the whole job done by the program itself. +It is as easy as that: Punch in the following and hit enter: + + ca:cL:1 ?> analyze auto abc.log xyz.mry + OR + ca:cL:1 ?> a auto abc.log xyz.mry + +This command tells the program the following: +- a[nalyze]: Let's analyze the whole tree... +- auto: ... but I'll let you do all the work for me... +- abc.log: ... when you're done, I'll be able to read and check everthing + that you did with the tree in the logfile abc.log... +- xyz.mry: ... and the final CART will be stored in xyz.mry. + +After that, hit enter three times in a row, or, if you want to experiment, +enter "no" for the specified units that you do NOT wish to cut. + +Note that of course you may use other names that abc.log and xyz.mry. +Note also that both the logfile and the cart file are stored in the voice +directory that you started the program in. +Thus, if you want the CART to be stored directly in the mary_files directory, +you may write something like: + + a auto test.log mary_files/testcart.mry + +That's all there is to it. +You won't even have to go get a cup of coffee because the algorithm +usually takes less than a second to complete. +(Which of course, if you want to change your copy of the CARTAnalyzer, +allows for creating dozens and hundreds of different(ly) pruned CARTs...) + +PLEASE NOTE THAT WHEN BACK IN THE COMMAND LINE, YOU ARE STILL USING THE +PRUNED CART (IT IS NOT RESET). BY EXITTING, THE CHANGES WILL NOT BE SAVED +TO THE ORIGINAL CART, SO IF YOU WANT TO REDO SOMETHING OR HAVE THE ORIGINAL +CART BACK, JUST a) RESTART THE PROGRAM or b) TYPE "load mary_files/cart.mry"! +THIS APPLIES TO ALL OPTIONS! + +Option b) + +If you wish for some more control in the whole process and still do not want +to touch the ca code, this is the right option for you. +But be warned: This WILL take some time to get used to, so if you +just want a quick job, go for option a) instead! +That said, this option is really neat since you can listen to all the units +in every leaf (the audio data), you can view every single leaf and have a list +of the units that the leaf contains and their respective float value +as described above. +The command is this: + + ca:cL:1 ?> analyze user abc.log xyz.mry + OR + ca:cL:1 ?> a user abc.log xyz.mry + +This command tells the program the following: +- a[nalyze]: Let's analyze the whole tree... +- user: ... and let me be the one to control everything and decide which units + should be cut from which leaves ... +- abc.log: ... when you're done, I'll be able to read and check everthing + that I did with the tree in the logfile abc.log... +- xyz.mry: ... and the final CART will be stored in xyz.mry. + +Please note that this mode can take a very, VERY long time depending on the +size of the voice. + +After you hit enter, you will be taken to a second command line which looks +like this: + + ca:aa:cL:1 ?> + +You will also be shown a lot of information, including the main commands you can use +for viewing the leafs, listening to them and cutting the units. +Also, the decision path will be shown for each leaf. +(The decision path tells you the path that MARY would go in this tree for this + particular leaf/sound as described at the beginning of this document, it looks + something like this: mary_phoneme==A - mary_stressed==1 - mary_xyz==x) + +Note that you can resume your work in this mode and therefore it's not really +necessary to give a cart file when you run this mode for the first time. +When you type exit in this mode, your current progress will be saved and you +can resume from there later on or even weeks later by typing + + ca:cL:1 ?> a resume + +or by typing "yes" when you repeat the a user abc xyz command. + +I used to think that this mode is close to self-explaining but I will now try to +explain the main point of it to you: + + +IMPORTANT NOTE: Help files +In the main(!) command line you can type + ca:cL:x ?> help [about/command] +Without parameters, the help file will show you all available commands for the main +command line, help about will display a short about text and help command goes into +details for the command that you want help for. + +Option c) (not recommended) + +This is the option that you probably do not want to use, ever. +You can, theoretically, do all the work in the main command line. +As already mentioned, I do not recommend this mode, but it does have +some nice features like text dumping of the cart, saving it to either +.mry or in CA format, or have an in-depth statistic about +the leaves and their units (particularly their float values) +logged to a file. + +If you really want this (or you just want some diagnosis data), type +"help" in the command line and look what's right for you. + +The last step: + +If you haven't already had the new CART dumped in something like xyz.mry, +you can now do by typing "dump xyz.mry", if you want to work on it again +some time later, type "save xyz" (you can use "load xyz" to load it after that). + +So when all is done and the CART has been successfully pruned, you just may +want to try it out by (for example) renaming the original cart to something +like cart.mry.bak and your new one to cart.mry in the mary_files directory. + +Congratulations! +That's it. +If you experience trouble, read the following ultra-short troubleshooting guide or +contact me at yukido@web.de (also feel free to contact me if you want to further understand +the code and/or change/improve it, I will be happy to help as much as I can) + +TROUBLESHOOTING: + +1) The automatic mode didn't cut any units / All units in every leaf have a float value of 0.0 + + You are probably using a non-float/old/MARY version of the cart. Try rebuilding the CART of the + voice with the new and improved CARTBuilder (search for ExtendedClassificationTree, see above). + Note that this program can load both versions of a CART and you can still f.e. print the statistics + for the MARY cart or textdump it. + +2) When I want to listen to a leaf, the program throws some strange Exception + + For audio playback we are currently using the ClipPlayer by jsresources.org (java sound resources). + Sadly, it does have its limits and if there are too many units in the leaf (some hundreds) + it just will not play them back. + Try playing back the outliers only (see help file) or implement some other method to play back + the audio by yourself ;) + Also, sometimes, it says something about "could not find a line" which I don't really know about. + In that case, check your system's sound configuration and the usual other audio programs stuff. + +3) Help! There's a "test.wav" in my voice directory (that I do not want there)! + + I CAN EXPLAIN...! + Everytime that you listen to the units of a leaf or just any audio that you can listen to + with the CARTAnalyzer, temporary wav data is stored in the test.wav. + That's what it is. + You can delete it. Nothing bad will happen. I promise. + +4) I am still confused by all this. + + Don't panic. Read this and the help files over and over, play with the program + (as long as you don't overwrite the mary_files/cart.mry everything should be just fine), + read the log files, read the code (it is not that much after all). + You'll get used to the program relatively fast with the learning-by-doing method + I guess. + +5) You suck. + + a) I know. + b) Get over it. + c) yukido@web.de + + + + + + + + + + + + + + + + + diff --git a/lib/marytts-5.2.1/doc/HnmTimelineFileFormat.odt b/lib/marytts-5.2.1/doc/HnmTimelineFileFormat.odt new file mode 100644 index 0000000..ee52f34 Binary files /dev/null and b/lib/marytts-5.2.1/doc/HnmTimelineFileFormat.odt differ diff --git a/lib/marytts-5.2.1/doc/MachineLearningClasses.odt b/lib/marytts-5.2.1/doc/MachineLearningClasses.odt new file mode 100644 index 0000000..970eea4 Binary files /dev/null and b/lib/marytts-5.2.1/doc/MachineLearningClasses.odt differ diff --git a/lib/marytts-5.2.1/doc/MaryReferences.odt b/lib/marytts-5.2.1/doc/MaryReferences.odt new file mode 100644 index 0000000..945fd5f Binary files /dev/null and b/lib/marytts-5.2.1/doc/MaryReferences.odt differ diff --git a/lib/marytts-5.2.1/doc/Model-Permission-Speaker.odt b/lib/marytts-5.2.1/doc/Model-Permission-Speaker.odt new file mode 100644 index 0000000..f2e522f Binary files /dev/null and b/lib/marytts-5.2.1/doc/Model-Permission-Speaker.odt differ diff --git a/lib/marytts-5.2.1/doc/NewLanguageWorkflow.odg b/lib/marytts-5.2.1/doc/NewLanguageWorkflow.odg new file mode 100644 index 0000000..1195e69 Binary files /dev/null and b/lib/marytts-5.2.1/doc/NewLanguageWorkflow.odg differ diff --git a/lib/marytts-5.2.1/doc/NewLanguageWorkflow.png b/lib/marytts-5.2.1/doc/NewLanguageWorkflow.png new file mode 100644 index 0000000..f000306 Binary files /dev/null and b/lib/marytts-5.2.1/doc/NewLanguageWorkflow.png differ diff --git a/lib/marytts-5.2.1/doc/SinusoidalHnmFlowchart.odg b/lib/marytts-5.2.1/doc/SinusoidalHnmFlowchart.odg new file mode 100644 index 0000000..39dbb35 Binary files /dev/null and b/lib/marytts-5.2.1/doc/SinusoidalHnmFlowchart.odg differ diff --git a/lib/marytts-5.2.1/doc/SinusoidalHnmFlowchart.png b/lib/marytts-5.2.1/doc/SinusoidalHnmFlowchart.png new file mode 100644 index 0000000..78a0d31 Binary files /dev/null and b/lib/marytts-5.2.1/doc/SinusoidalHnmFlowchart.png differ diff --git a/lib/marytts-5.2.1/doc/UnitDatabaseFormat.odt b/lib/marytts-5.2.1/doc/UnitDatabaseFormat.odt new file mode 100644 index 0000000..0b5a5a2 Binary files /dev/null and b/lib/marytts-5.2.1/doc/UnitDatabaseFormat.odt differ diff --git a/lib/marytts-5.2.1/doc/UnitSelectionAPI.odt b/lib/marytts-5.2.1/doc/UnitSelectionAPI.odt new file mode 100644 index 0000000..3df363b Binary files /dev/null and b/lib/marytts-5.2.1/doc/UnitSelectionAPI.odt differ diff --git a/lib/marytts-5.2.1/doc/VoiceConversionFlowchart.odg b/lib/marytts-5.2.1/doc/VoiceConversionFlowchart.odg new file mode 100644 index 0000000..dabd65b Binary files /dev/null and b/lib/marytts-5.2.1/doc/VoiceConversionFlowchart.odg differ diff --git a/lib/marytts-5.2.1/doc/VoiceConversionFlowchart.png b/lib/marytts-5.2.1/doc/VoiceConversionFlowchart.png new file mode 100644 index 0000000..b084d7f Binary files /dev/null and b/lib/marytts-5.2.1/doc/VoiceConversionFlowchart.png differ diff --git a/lib/marytts-5.2.1/doc/examples/client/MaryClient.py b/lib/marytts-5.2.1/doc/examples/client/MaryClient.py new file mode 100644 index 0000000..4656987 --- /dev/null +++ b/lib/marytts-5.2.1/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/lib/marytts-5.2.1/doc/examples/client/c++/Makefile b/lib/marytts-5.2.1/doc/examples/client/c++/Makefile new file mode 100644 index 0000000..e12c862 --- /dev/null +++ b/lib/marytts-5.2.1/doc/examples/client/c++/Makefile @@ -0,0 +1,44 @@ +########################################################################## +# 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 MaryDemo $(LIBS) + +%.o: %.cc + $(CC) $(CFLAGS) $(RFLAGS) -o $@ -c $< + +clean: + rm -rf *.o ./MaryDemo diff --git a/lib/marytts-5.2.1/doc/examples/client/c++/MaryClient.cc b/lib/marytts-5.2.1/doc/examples/client/c++/MaryClient.cc new file mode 100644 index 0000000..7b11a5c --- /dev/null +++ b/lib/marytts-5.2.1/doc/examples/client/c++/MaryClient.cc @@ -0,0 +1,253 @@ +/** + * 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 + +#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; + + int recv_bytes = 0; + char data [1024]; + + result.clear(); + + // receive the request result + do + { + recv_bytes = recv (maryDataSocket, data, sizeof(data), 0); + + if (recv_bytes == -1) + { + return -2; + } + else if (recv_bytes > 0) + { + result.append(data, recv_bytes); + } + } while (recv_bytes != 0); + + // receive the request error + do + { + data [0] = '\0'; + + recv_bytes = recv (maryInfoSocket, data, 1024, 0); + + if (recv_bytes == -1) + { + return -2; + } + else if (recv_bytes > 0) + { + cerr << endl << "Mary error code: " << data << endl; + return -3; + } + } while (recv_bytes != 0); + + // close all open sockets + close (maryInfoSocket); + close (maryDataSocket); + + return 0; +} diff --git a/lib/marytts-5.2.1/doc/examples/client/c++/MaryClient.cc.win b/lib/marytts-5.2.1/doc/examples/client/c++/MaryClient.cc.win new file mode 100644 index 0000000..66977c6 --- /dev/null +++ b/lib/marytts-5.2.1/doc/examples/client/c++/MaryClient.cc.win @@ -0,0 +1,290 @@ +/** + * 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. + */ + +#ifdef _WIN32 +// use compiler option -L//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/lib/marytts-5.2.1/doc/examples/client/c++/README.txt b/lib/marytts-5.2.1/doc/examples/client/c++/README.txt new file mode 100644 index 0000000..d860b70 --- /dev/null +++ b/lib/marytts-5.2.1/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/lib/marytts-5.2.1/doc/examples/client/maryclient-http.py b/lib/marytts-5.2.1/doc/examples/client/maryclient-http.py new file mode 100644 index 0000000..cf9782e --- /dev/null +++ b/lib/marytts-5.2.1/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/lib/marytts-5.2.1/doc/examples/client/maryclient.cgi b/lib/marytts-5.2.1/doc/examples/client/maryclient.cgi new file mode 100644 index 0000000..876a846 --- /dev/null +++ b/lib/marytts-5.2.1/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/lib/marytts-5.2.1/doc/examples/client/maryclient.pl b/lib/marytts-5.2.1/doc/examples/client/maryclient.pl new file mode 100644 index 0000000..8b3f5f8 --- /dev/null +++ b/lib/marytts-5.2.1/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/lib/marytts-5.2.1/doc/examples/client/maryclient.rb b/lib/marytts-5.2.1/doc/examples/client/maryclient.rb new file mode 100644 index 0000000..c4156cb --- /dev/null +++ b/lib/marytts-5.2.1/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/lib/marytts-5.2.1/doc/examples/client/maryclient.tcl b/lib/marytts-5.2.1/doc/examples/client/maryclient.tcl new file mode 100644 index 0000000..3a35823 --- /dev/null +++ b/lib/marytts-5.2.1/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/lib/marytts-5.2.1/doc/examples/client/texttospeechdemo.html b/lib/marytts-5.2.1/doc/examples/client/texttospeechdemo.html new file mode 100644 index 0000000..e788cf5 --- /dev/null +++ b/lib/marytts-5.2.1/doc/examples/client/texttospeechdemo.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +

MARY Web Client

+
+ + +
+ + + + + + + + + + + + + +
Input Type: + + Output Type: + +
+
Input Text:
+ +
+ +
Output Text:
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  + +
+ + Specify target features: + +
+
Voice: + + + +
+ +
+ +
Audio-Out:
+ + +
+ + +
+
+
+ + +Interactive documentation of the HTTP interface to MARY TTS + +
+ + diff --git a/lib/marytts-5.2.1/marytts-runtime/src/main/resources/marytts/server/http/mary.js b/lib/marytts-5.2.1/marytts-runtime/src/main/resources/marytts/server/http/mary.js new file mode 100644 index 0000000..f2ca54d --- /dev/null +++ b/lib/marytts-5.2.1/marytts-runtime/src/main/resources/marytts/server/http/mary.js @@ -0,0 +1,666 @@ + + +function GetXmlHttpObject() +{ + var xmlHttp=null; + try + { + // Firefox, Opera 8.0+, Safari + xmlHttp=new XMLHttpRequest(); + } + catch (e) + { + // Internet Explorer + try + { + xmlHttp=new ActiveXObject("Msxml2.XMLHTTP"); + } + catch (e) + { + xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); + } + } + return xmlHttp; +} + +function addOption(id, text) +{ + var opt=document.createElement('option'); + opt.text=text + var x=document.getElementById(id); + try { + x.add(opt,null); // standards compliant + } catch(ex) { + x.add(opt); // IE only + } +} + +function initForm() +{ + document.getElementById('VOICE_SELECTIONS').selectedIndex = 0; + document.getElementById('INPUT_TYPE').selectedIndex = 0; + document.getElementById('OUTPUT_TYPE').selectedIndex = 0; + document.getElementById('AUDIO_OUT').selectedIndex = 0; + document.getElementById('INPUT_TEXT').value = ''; + document.getElementById('OUTPUT_TEXT').value = ''; + document.getElementById('LOCALE').value = 'fill-me'; + document.getElementById('VOICE').value = 'fill-me'; + fillVoices(); + fillTypes(); + fillAudioFormats(); + fillEffects(); + setVisibilities("AUDIO"); + setAudio("WAVE_FILE"); +}; + + +function fillVoices() +{ + var xmlHttp = GetXmlHttpObject(); + if (xmlHttp==null) { + alert ("Your browser does not support AJAX!"); + return; + } + url = "voices"; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState==4) { + if (xmlHttp.status == 200) { + var response = xmlHttp.responseText; + var lines = response.split('\n'); + var localeElt = document.getElementById('LOCALE'); + var voiceElt = document.getElementById('VOICE'); + for (l in lines) { + var line = lines[l]; + if (line.length > 0) { + addOption("VOICE_SELECTIONS", line); + if (localeElt.value == 'fill-me') { + var items = line.split(' ', 2); + voiceElt.value = items[0]; + localeElt.value = items[1]; + updateInputText(true); + setModificationVisibility(null, "AUDIO"); // AUDIO is default on load + } + } + } + } else { + alert(xmlHttp.responseText); + } + } + }; + xmlHttp.open("GET", url, true); + xmlHttp.send(null); +} + +function fillTypes() +{ + var xmlHttp = GetXmlHttpObject(); + if (xmlHttp==null) { + alert ("Your browser does not support AJAX!"); + return; + } + url = "datatypes"; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState==4) { + if (xmlHttp.status == 200) { + var response = xmlHttp.responseText; + var lines = response.split('\n'); + for (l in lines) { + var line = lines[l]; + if (line.length > 0) { + var fields = line.split(' ', 1); + if (line.indexOf('INPUT') != -1) { + addOption("INPUT_TYPE", fields[0]); + if (fields[0]=="TEXT") { + var sel = document.getElementById("INPUT_TYPE"); + sel.selectedIndex = sel.length - 1; + updateInputText(true); + } + } + if (line.indexOf('OUTPUT') != -1) { + addOption("OUTPUT_TYPE", fields[0]); + if (fields[0]=="AUDIO") { + var sel = document.getElementById("OUTPUT_TYPE"); + sel.selectedIndex = sel.length - 1; + } + } + } + } + } else { + alert(xmlHttp.responseText); + } + } + }; + xmlHttp.open("GET", url, true); + xmlHttp.send(null); +} + +function fillAudioFormats() +{ + var xmlHttp = GetXmlHttpObject(); + if (xmlHttp==null) { + alert ("Your browser does not support AJAX!"); + return; + } + url = "audioformats"; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState==4) { + if (xmlHttp.status == 200) { + var response = xmlHttp.responseText; + var lines = response.split('\n'); + for (l in lines) { + var line = lines[l]; + if (line.length > 0) { + addOption("AUDIO_OUT", line); + if (line.indexOf("WAVE_FILE") != -1) { + document.getElementById("AUDIO_OUT").selectedIndex = document.getElementById("AUDIO_OUT").length - 1; + } + } + } + } else { + alert(xmlHttp.responseText); + } + } + }; + xmlHttp.open("GET", url, true); + xmlHttp.send(null); +} + + + +function fillEffects() +{ + var xmlHttp = GetXmlHttpObject(); + if (xmlHttp==null) { + alert ("Your browser does not support AJAX!"); + return; + } + url = "audioeffects"; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState==4) { + if (xmlHttp.status == 200) { + var response = xmlHttp.responseText; + var lines = response.split('\n'); + for (l in lines) { + var line = lines[l]; + if (line.length > 0) { + addEffect(line); + } + } + } else { + alert(xmlHttp.responseText); + } + } + }; + xmlHttp.open("GET", url, true); + xmlHttp.send(null); +} + +function addEffect(line) +{ + var iSpace = line.indexOf(" "); + var effect = line.substring(0, iSpace); + var params = line.substring(iSpace+1); + var effectsTable = document.getElementById('effectsTable'); + var row = effectsTable.insertRow(effectsTable.rows.length); + row.setAttribute("id", "effect_"+effect); + var checkboxCell = row.insertCell(0); + var checkbox = document.createElement("input"); + checkbox.setAttribute("type", "checkbox"); + checkbox.setAttribute("id", "effect_"+effect+"_selected"); + checkbox.setAttribute("name", "effect_"+effect+"_selected"); + checkbox.checked = false; + checkboxCell.appendChild(checkbox); + var nameCell = row.insertCell(1); + nameCell.innerHTML = effect; + var paramCell = row.insertCell(2); + var textarea = document.createElement("textarea"); + textarea.setAttribute("rows", "1"); + textarea.setAttribute("cols", "20"); + textarea.setAttribute("id", "effect_"+effect+"_parameters"); + textarea.setAttribute("name", "effect_"+effect+"_parameters"); + textarea.value = params; + paramCell.appendChild(textarea); + var defaultCell = row.insertCell(3); + var defaultButton = document.createElement("input"); + defaultButton.setAttribute("type", "button"); + defaultButton.setAttribute("id", "effect_"+effect+"_default"); + defaultButton.setAttribute("name", "effect_"+effect+"_default"); + defaultButton.setAttribute("value", "Default"); + defaultButton.setAttribute("onClick", "defaultEffectParams(this)"); + defaultCell.appendChild(defaultButton); + var helpCell = row.insertCell(4); + var helpButton = document.createElement("input"); + helpButton.setAttribute("type", "button"); + helpButton.setAttribute("id", "effect_"+effect+"_help"); + helpButton.setAttribute("name", "effect_"+effect+"_help"); + helpButton.setAttribute("value", "Help"); + helpButton.setAttribute("onClick", "helpEffect(this)"); + helpCell.appendChild(helpButton); +} + +function defaultEffectParams(button) +{ + var parts = button.getAttribute("id").split("_"); + var effect = parts[1]; + var xmlHttp = GetXmlHttpObject(); + if (xmlHttp==null) { + alert ("Your browser does not support AJAX!"); + return; + } + url = "audioeffect-default-param?effect="+effect; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState==4) { + if (xmlHttp.status == 200) { + document.getElementById('effect_'+effect+'_parameters').value = xmlHttp.responseText; + } else { + alert(xmlHttp.responseText); + } + } + }; + xmlHttp.open("GET", url, true); + xmlHttp.send(null); +} + +function helpEffect(button) +{ + var parts = button.getAttribute("id").split("_"); + var effect = parts[1]; + var xmlHttp = GetXmlHttpObject(); + if (xmlHttp==null) { + alert ("Your browser does not support AJAX!"); + return; + } + url = "audioeffect-help?effect="+effect; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState==4) { + if (xmlHttp.status == 200) { + document.getElementById('HELP_TEXT').value = xmlHttp.responseText; + } else { + alert(xmlHttp.responseText); + } + } + }; + xmlHttp.open("GET", url, true); + xmlHttp.send(null); +} + + +function inputTypeChanged() +{ + updateInputText(true); // replace input +} + +/** + * Update the current input text. + * Retrieves + * (1) the example text for the given input type and locale, as well as + * (2) the example texts for the current voice, if any. + * If there is a voice-specific example text and the input type is TEXT, use the voice-specific example; else, use the type example. + * Replace the content of input text only if replaceInput is true. + */ +function updateInputText(replaceInput) +{ + var inputTypeSelect = document.getElementById('INPUT_TYPE'); + var locale = document.getElementById('LOCALE').value; + if (inputTypeSelect.options.length == 0 || locale == 'fill-me') { // nothing to do yet + return; + } + var inputType = inputTypeSelect.options[inputTypeSelect.selectedIndex].text; + + // Keep track of AJAX concurrency across the two requests: + var retrievingVoiceExample = false; + var haveVoiceExample = false; + var retrievingTypeExample = false; + var typeExample = ""; + + // Only worth requesting type example if replaceInput is true: + if (replaceInput) { + var xmlHttp = GetXmlHttpObject(); + if (xmlHttp==null) { + alert ("Your browser does not support AJAX!"); + return; + } + var url = "exampletext?datatype=" + inputType + "&locale=" + locale; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState==4) { + if (xmlHttp.status == 200) { + typeExample = xmlHttp.responseText; + } else { + alert(xmlHttp.responseText); + } + retrievingTypeExample = false; + if (replaceInput && !retrievingTypeExample && !retrievingVoiceExample) { + if (haveVoiceExample) { + exampleChanged(); + } else { + document.getElementById('INPUT_TEXT').value = typeExample; + } + } + } + }; + retrievingTypeExample = true; + xmlHttp.open("GET", url, true); + xmlHttp.send(null); + } + + + + // Only worth requesting voice example if input type is TEXT: + if (inputType == "TEXT") { + var xmlHttp2 = GetXmlHttpObject(); + var voice = document.getElementById('VOICE').value; + var url2 = "exampletext?voice=" + voice; + xmlHttp2.onreadystatechange = function() { + if (xmlHttp2.readyState==4) { + if (xmlHttp2.status == 200) { + var examples = xmlHttp2.responseText; + + + + if (examples != "") { + haveVoiceExample = true; + document.getElementById("exampleTexts").style.display = 'inline'; + document.getElementById("exampleTexts").length = 0; + var lines = examples.split('\n'); + for (l in lines) { + var line = lines[l]; + if (line.length > 0) { + addOption("exampleTexts", line); + } + } + } else { + haveVoiceExample = false; + document.getElementById("exampleTexts").style.display = 'none'; + } + } else { + alert(xmlHttp.responseText); + } + retrievingVoiceExample = false; + if (replaceInput && !retrievingTypeExample && !retrievingVoiceExample) { + if (haveVoiceExample) { + exampleChanged(); + } else { + document.getElementById('INPUT_TEXT').value = typeExample; + } + } + } + }; + retrievingVoiceExample = true; + xmlHttp2.open("GET", url2, true); + xmlHttp2.send(null); + + } else{ // input type not text, hide examples, don't send request + document.getElementById("exampleTexts").style.display = 'none'; + } + + +} + +function getOutputType() { + var select = document.getElementById("OUTPUT_TYPE"); + var outputType = select.options[select.selectedIndex].text; + return outputType; +} + +function outputTypeChanged() +{ + var outputType = getOutputType(); + setVisibilities(outputType); + setModificationVisibility(null, outputType); +} + + + +function setVisibilities(outputType) +{ + if (outputType == "AUDIO") { + document.getElementById("outputSection").style.display = 'none'; + document.getElementById("audioEffectsSection").style.display = 'inline'; + document.getElementById("showHideEffectsButton").style.display = 'inline'; + //document.getElementById("helpSection").style.display = 'inline'; + document.getElementById("PROCESS").style.display = 'none'; + document.getElementById("SPEAK").style.display = 'inline'; + document.getElementById("audioDestination").style.display = 'inline'; + document.getElementById("showHideTargetFeatures").style.display = 'none'; + } else { + document.getElementById("outputSection").style.display = 'inline'; + document.getElementById("audioEffectsSection").style.display = 'none'; + document.getElementById("showHideEffectsButton").style.display = 'none'; + //document.getElementById("helpSection").style.display = 'none'; + document.getElementById("PROCESS").style.display = 'inline'; + document.getElementById("SPEAK").style.display = 'none'; + document.getElementById("audioDestination").style.display = 'none'; + if (outputType == "TARGETFEATURES" || outputType == "HALFPHONE_TARGETFEATURES") { + document.getElementById("showHideTargetFeatures").style.display = 'inline'; + } else { + document.getElementById("showHideTargetFeatures").style.display = 'none'; + } + if(inputType == "PHONEMES"){ + + } + } +}; + +function toggleEffectsVisibility() +{ + var currentVisibility = document.getElementById("innerAudioEffectsSection").style.display; + if (currentVisibility == 'none') { + document.getElementById("innerAudioEffectsSection").style.display = 'inline'; + document.getElementById("TOGGLE_EFFECTS").value = 'Hide Audio Effects'; + } else { + document.getElementById("innerAudioEffectsSection").style.display = 'none'; + document.getElementById("TOGGLE_EFFECTS").value = 'Show Audio Effects'; + } +} + +/** + * Set visibility of the modification checkbox so that it is shown if the selected voice is of type + * "unitselection" and the selected output type is one that requires AUDIO, and hidden otherwise. + * + * @param {} voiceType "unitselection", "hmm", etc. will be filled if null + * @param {} outputType "AUDIO", etc. will be filled if null + * @return + * @type + */ +function setModificationVisibility(voiceType, outputType) { + // check for unitselection voice: + if (voiceType == null) { + voiceType = getVoiceItems()[3]; + } + if (voiceType != "unitselection") { + document.getElementById("showHideModification").style.display = 'none'; + return; + } + + // otherwise check for output type requiring AUDIO: + if (outputType == null) { + outputType = getOutputType(); + } + // TODO: as long as there is no InfoRequestHandler that returns the output types which require AUDIO, just do: + var outputTypesWithAudio = new Array("AUDIO", "REALISED_ACOUSTPARAMS", "REALISED_DURATIONS", "PRAAT_TEXTGRID"); + var isOutputTypeWithAudio = false; + for (var i = 0; i < outputTypesWithAudio.length; i++) { + if (outputTypesWithAudio[i] == outputType) { + isOutputTypeWithAudio = true; + break; + } + } + if (isOutputTypeWithAudio) { + document.getElementById("showHideModification").style.display = 'inline'; + } else { + document.getElementById("showHideModification").style.display = 'none'; + } +} + +function getVoiceItems() { + var select = document.getElementById('VOICE_SELECTIONS'); + var voice = select.options[select.selectedIndex].text; + var items = voice.split(' '); + return items; +} + +function voiceChanged() +{ + var items = getVoiceItems(); + document.getElementById('VOICE').value = items[0]; + var newLocale = items[1]; + var prevLocale = document.getElementById('LOCALE').value; + if (prevLocale != newLocale) { + document.getElementById('LOCALE').value = newLocale; + updateInputText(true); // replace input + } else { + updateInputText(false); // do not replace input + } + var voiceType = items[3]; + setModificationVisibility(voiceType, null); +}; + +function exampleChanged() +{ + var select = document.getElementById('exampleTexts'); + var example = select.options[select.selectedIndex].text; + document.getElementById('INPUT_TEXT').value = example; +} + + +function audioOutChanged() +{ + var select = document.getElementById('AUDIO_OUT'); + setAudio(select.options[select.selectedIndex].text); + requestSynthesis(); +} + +function setAudio(value) +{ + document.getElementById('AUDIO').value = value; +} + + +function doSubmit() +{ + document.getElementById('maryWebClient').submit(); +} + +function requestSynthesis() +{ + var url = "process"; + var param = ""; + var maryForm=document.getElementById("maryWebClient"); + for (var i=0;i 0) { + value = element.options[element.selectedIndex].text; + } else { + value = ""; + } + } + else if (element.getAttribute("type") == "checkbox") { + // some special checkboxes that have nothing to do with effects: + if (element.id == "modification") { + // if modification is visible and selected, set OUTPUT_TYPE_PARAMS: + if (document.getElementById("showHideModification").style.display != 'none' && element.checked) { + key = "OUTPUT_TYPE_PARAMS"; + value = "MODIFICATION"; + } + } else if (element.id == "specifyTargetFeatures") { + // if target features is visible and selected and not empty, set OUTPUT_TYPE_PARAMS: + if (document.getElementById("showHideTargetFeatures").style.display != 'none' && element.checked) { + var targetFeatures = document.getElementById("targetFeatureList").value; + if (targetFeatures.length > 0) { + key = "OUTPUT_TYPE_PARAMS"; + value = targetFeatures; + } + } + } else { + value = element.checked ? "on" : ""; + } + } else { + value = element.value; + } + + if (key.length == 0) { + continue; // don't add keyless params! + } + + if (param.length > 0) param = param + "&"; + param = param + key + "=" + encodeURIComponent(value); + } + + var outputType = getOutputType(); + if (outputType == "AUDIO") { + //doSubmit(); + url = url + "?" + param; + var audioDestination = document.getElementById("audioDestination"); + while (audioDestination.childNodes.length > 0) { + audioDestination.removeChild(audioDestination.firstChild); + } + + // Check whether