diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..44dbcd6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pyc +.idea/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cd519f1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +sudo: required +language: generic +compiler: + - gcc +notifications: + email: + on_success: change + on_failure: always + recipients: + - ros-contributions@amazon.com +env: + matrix: + - ROS_DISTRO="kinetic" ROS_REPOSITORY_PATH=http://packages.ros.org/ros/ubuntu + - ROS_DISTRO="kinetic" ROS_REPOSITORY_PATH=http://packages.ros.org/ros-shadow-fixed/ubuntu + - ROS_DISTRO="lunar" ROS_REPOSITORY_PATH=http://packages.ros.org/ros/ubuntu + - ROS_DISTRO="lunar" ROS_REPOSITORY_PATH=http://packages.ros.org/ros-shadow-fixed/ubuntu + - ROS_DISTRO="melodic" ROS_REPOSITORY_PATH=http://packages.ros.org/ros/ubuntu + - ROS_DISTRO="melodic" ROS_REPOSITORY_PATH=http://packages.ros.org/ros-shadow-fixed/ubuntu +matrix: + allow_failures: + - env: ROS_DISTRO="lunar" ROS_REPOSITORY_PATH=http://packages.ros.org/ros/ubuntu + - env: ROS_DISTRO="lunar" ROS_REPOSITORY_PATH=http://packages.ros.org/ros-shadow-fixed/ubuntu + - env: ROS_DISTRO="melodic" ROS_REPOSITORY_PATH=http://packages.ros.org/ros/ubuntu + - env: ROS_DISTRO="melodic" ROS_REPOSITORY_PATH=http://packages.ros.org/ros-shadow-fixed/ubuntu +install: + - git clone https://github.com/ros-industrial/industrial_ci.git .ros_ci +script: + - .ros_ci/travis.sh diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 3b64466..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,4 +0,0 @@ -## Code of Conduct -This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). -For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 43b98b6..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,61 +0,0 @@ -# Contributing Guidelines - -Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional -documentation, we greatly value feedback and contributions from our community. - -Please read through this document before submitting any issues or pull requests to ensure we have all the necessary -information to effectively respond to your bug report or contribution. - - -## Reporting Bugs/Feature Requests - -We welcome you to use the GitHub issue tracker to report bugs or suggest features. - -When filing an issue, please check [existing open](https://github.com/aws/aws-ros-tts-ros1/issues), or [recently closed](https://github.com/aws/aws-ros-tts-ros1/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already -reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: - -* A reproducible test case or series of steps -* The version of our code being used -* Any modifications you've made relevant to the bug -* Anything unusual about your environment or deployment - - -## Contributing via Pull Requests -Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: - -1. You are working against the latest source on the *master* branch. -2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. -3. You open an issue to discuss any significant work - we would hate for your time to be wasted. - -To send us a pull request, please: - -1. Fork the repository. -2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass. -4. Commit to your fork using clear commit messages. -5. Send us a pull request, answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. - -GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and -[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). - - -## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws/aws-ros-tts-ros1/labels/help%20wanted) issues is a great place to start. - - -## Code of Conduct -This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). -For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. - - -## Security issue notifications -If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. - - -## Licensing - -See the [LICENSE](https://github.com/aws/aws-ros-tts-ros1/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. - -We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 14c7012..0000000 --- a/NOTICE +++ /dev/null @@ -1,2 +0,0 @@ -AWS Ros Tts Ros1 -Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/README.md b/README.md index 1bb2e76..37392d7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,246 @@ -## AWS Ros Tts Ros1 +# tts -ROS packages for facilitating text-to-speech and the use of Amazon Polly. -## License +## Overview +The `tts` ROS node enables a robot to speak with a human voice by providing a Text-To-Speech service. +Out of the box this package listens to a speech topic, submits text to the Amazon Polly cloud service to generate an audio stream file, +retrieves the audio stream from Amazon Polly, and plays the audio stream via the default output device. +The nodes can be configured to use different voices as well as custom lexicons and SSML tags which enable you to control aspects of speech, +such as pronunciation, volume, pitch, speed rate, etc. A [sample ROS application] with this node, +and more details on speech customization are available within the [Amazon Polly documentation]. -This library is licensed under the Apache 2.0 License. +**Amazon Polly Summary**: Amazon Polly is a service that turns text into lifelike speech, allowing you to create applications that talk, +and build entirely new categories of speech-enabled products. Amazon Polly is a Text-to-Speech service that uses advanced deep learning technologies to synthesize speech that sounds like a human voice. +With dozens of lifelike voices across a variety of languages, you can select the ideal voice and build speech-enabled applications that work in many different countries. + +**Features in Active Development**: +- Offline TTS + +### License +The source code is released under an [Apache 2.0]. + +**Author**: AWS RoboMaker
+**Affiliation**: [Amazon Web Services (AWS)]
+**Maintainer**: AWS RoboMaker, ros-contributions@amazon.com + +### Supported ROS Distributions +- Kinetic +- Lunar +- Melodic + + +## Installation + +### AWS Credentials +You will need to create an AWS Account and configure the credentials to be able to communicate with AWS services. You may find [AWS Configuration and Credential Files] helpful. + +This node will require the following AWS account IAM role permissions: +- `polly:SynthesizeSpeech` + +### Build and Test + +#### Build from Source + +If you test this package on versions of Ubuntu older than 18.x (16.04 for example), please upgrade `mock` to the latest version by using `pip`. + + sudo apt-get python-pip + pip install -U mock requests + +Create a ROS workspace and a source directory + + mkdir -p ~/ros-workspace/src + +To install from source, clone the latest version from master branch and compile the package + +- Clone the package into the source directory + + cd ~/ros-workspace + git clone https://github.com/aws-robotics/tts-ros1.git + +- Install dependencies + + cd ~/ros-workspace && sudo apt-get update + rosdep install --from-paths src --ignore-src -r -y + +- Install the packages + + cd ~/ros-workspace && colcon build + +- Configure ROS library Path + + source ~/ros-workspace/install/setup.bash + +- Build and run the unit tests + + colcon test --packages-select tts && colcon test-result --all + +#### Test on Containers/Virtual Machines + +Even if your container or virtual machine does not have audio device, you can still test TTS by leveraging an audio server. + +The following is an example setup on a MacBook with PulseAudio as the audio server. +If you are new to PulseAudio, you may want to read the [PulseAudio Documentation]. + +**Step 1: Start PulseAudio on your laptop** + +After installation, start the audio server with *module-native-protocol-tcp* loaded: + + pulseaudio --load=module-native-protocol-tcp --exit-idle-time=-1 --log-target=stderr -v + +Note the extra arguments `-v` and `--log-target` are used for easier troubleshooting. + +**Step 2: Run TTS nodes in container** + +In your container, make sure you set the right environment variables. +For example, you can start the container using `docker run -it -e PULSE_SERVER=docker.for.mac.localhost ubuntu:16.04`. + +Then you will be able to run ROS nodes in the container and hear the audio from your laptop speakers. + +**Troubleshooting** + +If your laptop has multiple audio output devices, make sure the right one has the right volume. +This command will give you a list of output devices and tell you which one has been selected: + + pacmd list-sinks | grep -E '(index:|name:|product.name)' + +## Launch Files +An example launch file called `sample_application.launch` is provided. + + +## Usage + +### Run the node +- **Plain text** + - `roslaunch tts sample_application.launch` + - `rosrun tts voicer.py 'Hello World'` + +- **SSML** + - `roslaunch tts sample_application.launch` + - `rosrun tts voicer.py 'Mary has a little lamb.' '{"text_type":"ssml"}'` + + +## Configuration File and Parameters +| Parameter Name | Type | Description | +| -------------- | ---- | ----------- | +| polly_action | *string* | Currently only one action named `SynthesizeSpeech` is supported. | +| text | *string* | The text to be synthesized. It can be plain text or SSML. See also `text_type`. | +| text_type | *string* | A user can choose from `text` and `ssml`. Default: `text`. | +| voice_id | *string* | The list of supported voices can be found on [official Amazon Polly document]. Default: Joanna | +| output_format | *string* | Valid formats are `ogg_vorbis`, `mp3` and `pcm`. Default: `ogg_vorbis` | +| output_path | *string* | The audio data will be saved as a local file for playback and reuse/inspection purposes. This parameter is to provide a preferred path to save the file. Default: `.` | +| sample_rate | *string* | Note `16000` is a valid sample rate for all supported formats. Default: `16000`. | + + +## Performance and Benchmark Results +We evaluated the performance of this node by runnning the followning scenario on a Raspberry Pi 3 Model B: +- Launch a baseline graph containing the talker and listener nodes from the [roscpp_tutorials package](https://wiki.ros.org/roscpp_tutorials), plus two additional nodes that collect CPU and memory usage statistics. Allow the nodes to run for 60 seconds. +- Launch the nodes `polly_node`, `synthesizer_node` and `tts_node` by using the launch file `sample_application.launch` as described above. At the same time, perform several calls to the action `tts/action/Speech.action` using the `voicer.py` script descried above, by running the following script in the background: + +```bash +rosrun tts voicer.py 'Amazon Polly is a Text-to-Speech (TTS) cloud service' '{"text_type":"ssml"}' ; sleep 1 +rosrun tts voicer.py 'that converts text into lifelike speech' '{"text_type":"ssml"}' ; sleep 1 +rosrun tts voicer.py 'You can use Amazon Polly to develop applications that increase engagement and accessibility' '{"text_type":"ssml"}' ; sleep 1 +rosrun tts voicer.py 'Amazon Polly supports multiple languages and includes a variety of lifelike voices' '{"text_type":"ssml"}' ; sleep 1 +rosrun tts voicer.py 'so you can build speech-enabled applications that work in multiple locations' '{"text_type":"ssml"}' ; sleep 1 +rosrun tts voicer.py 'and use the ideal voice for your customers' '{"text_type":"ssml"}' ; sleep 1 +``` + +- Allow the nodes to run for 180 seconds. +- Terminate the `polly_node`, `synthesizer_node` and `tts_node` nodes, and allow the reamaining nodes to run for 60 seconds. + +The following graph shows the CPU usage during that scenario. The 1 minute average CPU usage starts at 16.75% during the launch of the baseline graph, and stabilizes at 6%. When we launch the Polly nodes around second 85, the 1 minute average CPU increases up to a peak of 22.25% and stabilizes around 20%. After we stop making requests with the script `voicer.py` around second 206 the 1 minute average CPU usage moves to around 12%, and decreases gradually, and goes down again to 2.5 % after we stop the Polly nodes at the end of the scenario. + +![cpu](wiki/images/cpu.svg) + +The following graph shows the memory usage during that scenario. We start with a memory usage of around 227 MB that increases to around 335 MB (+47.58%) when we lanch the Polly nodes around second 85, and gets to a peak of 361 MB (+59% wrt. initial value) while we are calling the script `voicer.py`. The memory usage goes back to the initial values after stopping the Polly nodes. + +![memory](wiki/images/memory.svg) + + +## Nodes + +### polly +Polly node is the engine for the synthesizing job. It provides user-friendly yet powerful APIs so a user doesn't have to deal with technical details of AWS service calls. + +#### Services +- **`polly (tts/Polly)`** + + Call the service to use Amazon Polly to synthesize the audio. + +#### Reserved for future usage +- `language_code (string, default: None)` + + A user doesn't have to provide a language code and this is reserved for future usage. + +- `lexicon_content (string, default: None)` + +- `lexicon_name (string, default: None)` + +- `lexicon_names (string[], default: empty)` + +- `speech_mark_types (string[], default: empty)` + +- `max_results (uint32, default: None)` + +- `next_token (string, default: None)` + +- `sns_topic_arn (string, default: None)` + +- `task_id (string, default: None)` + +- `task_status (string, default: iNone)` + +- `output_s3_bucket_name (string, default: None)` + +- `output_s3_key_prefix (string, default: None)` + +- `include_additional_language_codes (bool, default: None)` + +### synthesizer node + +#### Services +- **`synthesizer (tts/Synthesizer)`** + + Call the service to synthesize. + +#### Parameters + +- **`text (string)`** + + The text to be synthesized. + +- **`metadata (string, JSON format)`** + + Optional, for user to have control over how synthesis happens. + +### tts node + +#### Action + +- **`speech`** + +#### Parameters + +- **`text (string)`** + + The text to be synthesized. + +- **`metadata (string, JSON format)`** + + Optional, for user to have control over how synthesis happens. + + +## Bugs & Feature Requests +Please contact the team directly if you would like to request a feature. + +Please report bugs in [Issue Tracker]. + + +[AWS Configuration and Credential Files]: https://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html +[Amazon Polly documentation]: https://docs.aws.amazon.com/polly/latest/dg/what-is.html +[Amazon Web Services (AWS)]: https://aws.amazon.com/ +[Apache 2.0]: https://aws.amazon.com/apache-2-0/ +[Issue Tracker]: https://github.com/aws-robotics/tts-ros1/issues +[PulseAudio Documentation]: https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/ +[official Amazon Polly document]: https://docs.aws.amazon.com/polly/latest/dg/voicelist.html +[sample ROS application]: https://github.com/aws-robotics/aws-robomaker-sample-application-voiceinteraction diff --git a/tts/CMakeLists.txt b/tts/CMakeLists.txt new file mode 100644 index 0000000..e5a05e9 --- /dev/null +++ b/tts/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 2.8.3) +project(tts) + +find_package(catkin REQUIRED COMPONENTS actionlib_msgs message_generation rospy rosunit rostest std_msgs sound_play) + +catkin_python_setup() + +################################################ +## Declare ROS messages, services and actions ## +################################################ + +## Generate services in the 'srv' folder +add_service_files(FILES Synthesizer.srv Polly.srv) + +## Generate actions in the 'action' folder +add_action_files(FILES Speech.action) + +## Generate added messages and services with any dependencies listed here +generate_messages(DEPENDENCIES actionlib_msgs std_msgs) + +################################### +## catkin specific configuration ## +################################### +## The catkin_package macro generates cmake config files for your package +## Declare things to be passed to dependent projects +## LIBRARIES: libraries you create in this project that dependent projects also need +## CATKIN_DEPENDS: catkin_packages dependent projects also need +## DEPENDS: system dependencies of this project that dependent projects also need +catkin_package( + LIBRARIES tts + CATKIN_DEPENDS actionlib_msgs message_runtime rospy std_msgs +) + +############# +## Install ## +############# + +# all install targets should use catkin DESTINATION variables +# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html + +## Mark executable scripts (Python etc.) for installation +## in contrast to setup.py, you can choose the destination +install(PROGRAMS + scripts/polly_node.py + scripts/synthesizer_node.py + scripts/tts_node.py + scripts/voicer.py + DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) + +install(DIRECTORY + config + launch + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +) + +############# +## Testing ## +############# +if(CATKIN_ENABLE_TESTING) + ## Add folders to be run by python nosetests + catkin_add_nosetests(test/test_unit_synthesizer.py) + catkin_add_nosetests(test/test_unit_polly.py) + + if(BUILD_AWS_TESTING) + find_package(rostest REQUIRED COMPONENTS tts) + add_rostest(test/integration_tests.test DEPENDENCIES ${tts_EXPORTED_TARGETS}) + endif() +endif() + + diff --git a/LICENSE b/tts/LICENSE.txt similarity index 99% rename from LICENSE rename to tts/LICENSE.txt index d645695..d8a44c5 100644 --- a/LICENSE +++ b/tts/LICENSE.txt @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2018 Amazon.com, Inc. or its affiliates Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/tts/NOTICE.txt b/tts/NOTICE.txt new file mode 100644 index 0000000..13033fe --- /dev/null +++ b/tts/NOTICE.txt @@ -0,0 +1,4 @@ +Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +This product includes software developed by +Amazon Technologies, Inc (http://www.amazon.com/). diff --git a/tts/action/Speech.action b/tts/action/Speech.action new file mode 100644 index 0000000..2da3ffe --- /dev/null +++ b/tts/action/Speech.action @@ -0,0 +1,9 @@ +#goal definition +string text +string metadata +--- +#result definition +string response +--- +#feedback +string data diff --git a/tts/config/sample_configuration.yaml b/tts/config/sample_configuration.yaml new file mode 100644 index 0000000..4c74e77 --- /dev/null +++ b/tts/config/sample_configuration.yaml @@ -0,0 +1,6 @@ +# This is the AWS Client Configuration used by the AWS service client in the Node. If given the node will load the +# provided configuration when initializing the client. +aws_client_configuration: + # Specifies where you want the client to communicate. Examples include us-east-1 or us-west-1. You must ensure that + # the service you want to use has an endpoint in the region you configure. + region: "us-west-2" diff --git a/tts/launch/sample_application.launch b/tts/launch/sample_application.launch new file mode 100644 index 0000000..42137f3 --- /dev/null +++ b/tts/launch/sample_application.launch @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/tts/launch/tts_polly.launch b/tts/launch/tts_polly.launch new file mode 100644 index 0000000..0e6fcec --- /dev/null +++ b/tts/launch/tts_polly.launch @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tts/package.xml b/tts/package.xml new file mode 100644 index 0000000..1e3d80f --- /dev/null +++ b/tts/package.xml @@ -0,0 +1,38 @@ + + + tts + 1.0.0 + Package enabling a robot to speak with a human voice by providing a Text-To-Speech ROS service + http://wiki.ros.org/tts + + AWS RoboMaker + AWS RoboMaker + + Apache 2.0 + + catkin + + actionlib_msgs + message_generation + rospy + std_msgs + python-boto3 + sound_play + rosunit + rostest + + actionlib_msgs + rospy + std_msgs + sound_play + + actionlib_msgs + rospy + std_msgs + message_runtime + python-boto3 + sound_play + + rosunit + rostest + diff --git a/tts/scripts/polly_node.py b/tts/scripts/polly_node.py new file mode 100755 index 0000000..d07dfd3 --- /dev/null +++ b/tts/scripts/polly_node.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if __name__ == '__main__': + import tts.amazonpolly + tts.amazonpolly.main() diff --git a/tts/scripts/synthesizer_node.py b/tts/scripts/synthesizer_node.py new file mode 100755 index 0000000..937870b --- /dev/null +++ b/tts/scripts/synthesizer_node.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if __name__ == "__main__": + import tts.synthesizer + tts.synthesizer.main() diff --git a/tts/scripts/tts_node.py b/tts/scripts/tts_node.py new file mode 100755 index 0000000..b8bdd6a --- /dev/null +++ b/tts/scripts/tts_node.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""A very simple Action Server that does TTS. + +It is a combination of a synthesizer and a player. Being an action server, it can be used in two different manners. + +1. Play and wait for it to finish +--------------------------------- + +A user can choose to be blocked until the audio playing is done. This is especially useful in interactive scenarios. + +Example:: + + rospy.init_node('tts_action_client') + client = actionlib.SimpleActionClient('tts', SpeechAction) + client.wait_for_server() + goal = SpeechGoal() + goal.text = 'Let me ask you a question, please give me your answer.' + client.send_goal(goal) + client.wait_for_result() + + # start listening to a response or waiting for some input to continue the interaction + +2. Play and forget +------------------ + +A user can also choose not to wait:: + + rospy.init_node('tts_action_client') + client = actionlib.SimpleActionClient('tts', SpeechAction) + client.wait_for_server() + goal = SpeechGoal() + goal.text = 'Let me talk, you can to something else in the meanwhile.' + client.send_goal(goal) + +This is useful when the robot wants to do stuff while the audio is being played. For example, a robot may start to +read some instructions and immediately get ready for any input. +""" + +import json + +import actionlib +import rospy +from tts.msg import SpeechAction, SpeechResult +from tts.srv import Synthesizer + +from sound_play.libsoundplay import SoundClient + + +def play(filename): + """plays the wav or ogg file using sound_play""" + SoundClient(blocking=True).playWave(filename) + + +def do_synthesize(goal): + """calls synthesizer service to do the job""" + rospy.wait_for_service('synthesizer') + synthesize = rospy.ServiceProxy('synthesizer', Synthesizer) + return synthesize(goal.text, goal.metadata) + + +def finish_with_result(s): + """responds the client""" + tts_server_result = SpeechResult(s) + server.set_succeeded(tts_server_result) + rospy.loginfo(tts_server_result) + + +def do_speak(goal): + """The action handler. + + Note that although it responds to client after the audio play is finished, a client can choose + not to wait by not calling ``SimpleActionClient.waite_for_result()``. + """ + rospy.loginfo('speech goal: {}'.format(goal)) + + res = do_synthesize(goal) + rospy.loginfo('synthesizer returns: {}'.format(res)) + + try: + r = json.loads(res.result) + except Exception as e: + s = 'Expecting JSON from synthesizer but got {}'.format(res.result) + rospy.logerr('{}. Exception: {}'.format(s, e)) + finish_with_result(s) + return + + result = '' + + if 'Audio File' in r: + audio_file = r['Audio File'] + rospy.loginfo('Will play {}'.format(audio_file)) + play(audio_file) + result = audio_file + + if 'Exception' in r: + result = '[ERROR] {}'.format(r) + rospy.logerr(result) + + finish_with_result(result) + + +if __name__ == '__main__': + rospy.init_node('tts_node') + server = actionlib.SimpleActionServer('tts', SpeechAction, do_speak, False) + server.start() + rospy.spin() diff --git a/tts/scripts/voicer.py b/tts/scripts/voicer.py new file mode 100755 index 0000000..9bbd551 --- /dev/null +++ b/tts/scripts/voicer.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Usage: + +(assuming TTS action server has been started via `roslaunch tts tts_polly.launch`) + +Plain text:: + + $ rosrun tts voicer.py 'Hello World' + +SSML:: + + $ rosrun tts voicer.py \ + 'Mary has a little lamb.' \ + '{"text_type":"ssml"}' +""" + + +import sys +import actionlib +import rospy +from tts.msg import SpeechAction, SpeechGoal + + +if __name__ == '__main__': + rospy.init_node('tts_action_client') + client = actionlib.SimpleActionClient('tts', SpeechAction) + client.wait_for_server() + + goal = SpeechGoal() + + goal.text = sys.argv[1] if len(sys.argv) > 1 else 'I got no idea.' + goal.metadata = sys.argv[2] if len(sys.argv) > 2 else '' + + client.send_goal(goal) + client.wait_for_result() + print('\n' + client.get_result().response) diff --git a/tts/setup.py b/tts/setup.py new file mode 100644 index 0000000..66098bd --- /dev/null +++ b/tts/setup.py @@ -0,0 +1,33 @@ +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +import os +from distutils.core import setup +from catkin_pkg.python_setup import generate_distutils_setup + + +# ROS PACKAGING +# using distutils : https://docs.python.org/2/distutils +# fetch values from package.xml +setup_args = generate_distutils_setup( + packages=[ + 'tts', + ], + package_dir={ + '': 'src', + }, + package_data={ + '': ['data/*.ogg', 'data/models/polly/2016-06-10/*.json'] + }, +) +setup(**setup_args) diff --git a/tts/src/tts/__init__.py b/tts/src/tts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tts/src/tts/amazonpolly.py b/tts/src/tts/amazonpolly.py new file mode 100755 index 0000000..d6da9f4 --- /dev/null +++ b/tts/src/tts/amazonpolly.py @@ -0,0 +1,428 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +import json +import os +import sys +import wave +import traceback +import requests +from boto3 import Session +from botocore.credentials import CredentialProvider, RefreshableCredentials +from botocore.session import get_session +from botocore.exceptions import UnknownServiceError +from contextlib import closing +from optparse import OptionParser + +import rospy +from tts.srv import Polly, PollyRequest, PollyResponse + + +def get_ros_param(param, default=None): + try: + key = rospy.search_param(param) + return default if key is None else rospy.get_param(key, default) + except Exception as e: + rospy.logwarn('Failed to get ros param {}, will use default {}. Exception: '.format(param, default, e)) + return default + + +class AwsIotCredentialProvider(CredentialProvider): + METHOD = 'aws-iot' + CANONICAL_NAME = 'customIoTwithCertificate' + + DEFAULT_AUTH_CONNECT_TIMEOUT_MS = 5000 + DEFAULT_AUTH_TOTAL_TIMEOUT_MS = 10000 + + def __init__(self): + super(AwsIotCredentialProvider, self).__init__() + self.ros_param_prefix = 'iot/' + + def get_param(self, param, default=None): + return get_ros_param(self.ros_param_prefix + param, default) + + def retrieve_credentials(self): + try: + cert_file = self.get_param('certfile') + key_file = self.get_param('keyfile') + endpoint = self.get_param('endpoint') + role_alias = self.get_param('role') + connect_timeout = self.get_param('connect_timeout_ms', self.DEFAULT_AUTH_CONNECT_TIMEOUT_MS) + total_timeout = self.get_param('total_timeout_ms', self.DEFAULT_AUTH_TOTAL_TIMEOUT_MS) + thing_name = self.get_param('thing_name', '') + + if any(v is None for v in (cert_file, key_file, endpoint, role_alias, thing_name)): + return None + + headers = {'x-amzn-iot-thingname': thing_name} if len(thing_name) > 0 else None + url = 'https://{}/role-aliases/{}/credentials'.format(endpoint, role_alias) + timeout = (connect_timeout, total_timeout - connect_timeout) # see also: urllib3/util/timeout.py + + response = requests.get(url, cert=(cert_file, key_file), headers=headers, timeout=timeout) + d = response.json()['credentials'] + + rospy.loginfo('Credentials expiry time: {}'.format(d['expiration'])) + + return { + 'access_key': d['accessKeyId'], + 'secret_key': d['secretAccessKey'], + 'token': d['sessionToken'], + 'expiry_time': d['expiration'], + } + except Exception as e: + rospy.logwarn('Failed to fetch credentials from AWS IoT: {}'.format(e)) + return None + + def load(self): + return RefreshableCredentials.create_from_metadata( + self.retrieve_credentials(), + self.retrieve_credentials, + 'aws-iot-with-certificate' + ) + + +class AmazonPolly: + """A TTS engine that can be used in two different ways. + + Usage + ----- + + 1. It can run as a ROS service node. + + Start a polly node:: + + $ rosrun tts polly_node.py + + Call the service from command line:: + + $ rosservice call /polly SynthesizeSpeech 'hello polly' '' '' '' '' '' '' '' '' [] [] 0 '' '' '' '' '' '' false + + Call the service programmatically:: + + from tts.srv import Polly + rospy.wait_for_service('polly') + polly = rospy.ServiceProxy('polly', Polly) + res = polly(**kw) + + 2. It can also be used as a normal python class:: + + AmazonPolly().synthesize(text='hi polly') + + PollyRequest supports many parameters, but the majority of the users can safely ignore most of them and just + use the vanilla version which involves only one argument, ``text``. + + If in some use cases more control is needed, SSML will come handy. Example:: + + AmazonPolly().synthesize( + text='Mary has a little lamb.', + text_type='ssml' + ) + + A user can also control the voice, output format and so on. Example:: + + AmazonPolly().synthesize( + text='Mary has a little lamb.', + text_type='ssml', + voice_id='Joey', + output_format='mp3', + output_path='/tmp/blah' + ) + + + Parameters + ---------- + + Among the parameters defined in Polly.srv, the following are supported while others are reserved for future. + + * polly_action : currently only ``SynthesizeSpeech`` is supported + * text : the text to speak + * text_type : can be either ``text`` (default) or ``ssml`` + * voice_id : any voice id supported by Amazon Polly, default is Joanna + * output_format : ogg (default), mp3 or pcm + * output_path : where the audio file is saved + * sample_rate : default is 16000 for pcm or 22050 for mp3 and ogg + + The following are the reserved ones. Note that ``language_code`` is rarely needed (this may seem counter-intuitive). + See official Amazon Polly documentation for details (link can be found below). + + * language_code + * lexicon_content + * lexicon_name + * lexicon_names + * speech_mark_types + * max_results + * next_token + * sns_topic_arn + * task_id + * task_status + * output_s3_bucket_name + * output_s3_key_prefix + * include_additional_language_codes + + + Links + ----- + + Amazon Polly documentation: https://docs.aws.amazon.com/polly/latest/dg/API_SynthesizeSpeech.html + + """ + + def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, aws_session_token=None, region_name=None): + if region_name is None: + region_name = get_ros_param('aws_client_configuration/region', default='us-west-2') + + self.polly = self._get_polly_client(aws_access_key_id, aws_secret_access_key, aws_session_token, region_name) + self.default_text_type = 'text' + self.default_voice_id = 'Joanna' + self.default_output_format = 'ogg_vorbis' + self.default_output_folder = '.' + self.default_output_file_basename = 'output' + + def _get_polly_client(self, aws_access_key_id=None, aws_secret_access_key=None, aws_session_token=None, + region_name=None, with_service_model_patch=False): + """Note we get a new botocore session each time this function is called. + This is to avoid potential problems caused by inner state of the session. + """ + botocore_session = get_session() + + if with_service_model_patch: + # Older versions of botocore don't have polly. We can possibly fix it by appending + # extra path with polly service model files to the search path. + current_dir = os.path.dirname(os.path.abspath(__file__)) + service_model_path = os.path.join(current_dir, 'data', 'models') + botocore_session.set_config_variable('data_path', service_model_path) + rospy.loginfo('patching service model data path: {}'.format(service_model_path)) + + botocore_session.get_component('credential_provider').insert_after('boto-config', AwsIotCredentialProvider()) + + botocore_session.user_agent_extra = self._generate_user_agent_suffix() + + session = Session(aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, region_name=region_name, + botocore_session=botocore_session) + + try: + return session.client("polly") + except UnknownServiceError: + # the first time we reach here, we try to fix the problem + if not with_service_model_patch: + return self._get_polly_client(aws_access_key_id, aws_secret_access_key, aws_session_token, region_name, + with_service_model_patch=True) + else: + # we have tried our best, time to panic + rospy.logerr('Amazon Polly is not available. Please install the latest boto3.') + raise + + def _generate_user_agent_suffix(self): + exec_env = get_ros_param('exec_env', 'AWS_RoboMaker').strip() + if 'AWS_RoboMaker' in exec_env: + ver = get_ros_param('robomaker_version', None) + if ver: + exec_env += '-' + ver.strip() + ros_distro = get_ros_param('rosdistro', 'Unknown_ROS_DISTRO').strip() + ros_version = get_ros_param('rosversion', 'Unknown_ROS_VERSION').strip() + return 'exec-env/{} ros-{}/{}'.format(exec_env, ros_distro, ros_version) + + def _pcm2wav(self, audio_data, wav_filename, sample_rate): + """per Amazon Polly official doc, the pcm in a signed 16-bit, 1 channel (mono), little-endian format.""" + wavf = wave.open(wav_filename, 'w') + wavf.setframerate(int(sample_rate)) + wavf.setnchannels(1) # 1 channel + wavf.setsampwidth(2) # 2 bytes == 16 bits + wavf.writeframes(audio_data) + wavf.close() + + def _make_audio_file_fullpath(self, output_path, output_format): + """Makes a full path for audio file based on given output path and format. + + If ``output_path`` doesn't have a path, current path is used. + + :param output_path: the output path received + :param output_format: the audio format, e.g., mp3, ogg_vorbis, pcm + :return: a full path for the output audio file. File ext will be constructed from audio format. + """ + head, tail = os.path.split(output_path) + if not head: + head = self.default_output_folder + if not tail: + tail = self.default_output_file_basename + + file_ext = {'pcm': '.wav', 'mp3': '.mp3', 'ogg_vorbis': '.ogg'}[output_format.lower()] + if not tail.endswith(file_ext): + tail += file_ext + + return os.path.realpath(os.path.join(head, tail)) + + def _synthesize_speech_and_save(self, request): + """Calls Amazon Polly and writes the returned audio data to a local file. + + To make it practical, three things will be returned in a JSON form string, which are audio file path, + audio type and Amazon Polly response metadata. + + If the Amazon Polly call fails, audio file name will be an empty string and audio type will be "N/A". + + Please see https://boto3.readthedocs.io/reference/services/polly.html#Polly.Client.synthesize_speech + for more details on Amazon Polly API. + + :param request: an instance of PollyRequest + :return: a string in JSON form with two attributes, "Audio File" and "Amazon Polly Response". + """ + kws = { + 'LexiconNames': request.lexicon_names if request.lexicon_names else [], + 'OutputFormat': request.output_format if request.output_format else self.default_output_format, + 'SampleRate': request.sample_rate, + 'SpeechMarkTypes': request.speech_mark_types if request.speech_mark_types else [], + 'Text': request.text, + 'TextType': request.text_type if request.text_type else self.default_text_type, + 'VoiceId': request.voice_id if request.voice_id else self.default_voice_id + } + + if not kws['SampleRate']: + kws['SampleRate'] = '16000' if kws['OutputFormat'].lower() == 'pcm' else '22050' + + rospy.loginfo('Amazon Polly Request: {}'.format(kws)) + response = self.polly.synthesize_speech(**kws) + rospy.loginfo('Amazon Polly Response: {}'.format(response)) + + if "AudioStream" in response: + audiofile = self._make_audio_file_fullpath(request.output_path, kws['OutputFormat']) + rospy.loginfo('will save audio as {}'.format(audiofile)) + + with closing(response["AudioStream"]) as stream: + if kws['OutputFormat'].lower() == 'pcm': + self._pcm2wav(stream.read(), audiofile, kws['SampleRate']) + else: + with open(audiofile, "wb") as f: + f.write(stream.read()) + + audiotype = response['ContentType'] + else: + audiofile = '' + audiotype = 'N/A' + + return json.dumps({ + 'Audio File': audiofile, + 'Audio Type': audiotype, + 'Amazon Polly Response Metadata': str(response['ResponseMetadata']) + }) + + def _dispatch(self, request): + """Amazon Polly supports a number of APIs. This will call the right one based on the content of request. + + Currently "SynthesizeSpeech" is the only recognized action. Basically this method just delegates the work + to ``self._synthesize_speech_and_save`` and returns the result as is. It will simply raise if a different + action is passed in. + + :param request: an instance of PollyRequest + :return: whatever returned by the delegate + """ + actions = { + 'SynthesizeSpeech': self._synthesize_speech_and_save + # ... more actions could go in here ... + } + + if request.polly_action not in actions: + raise RuntimeError('bad or unsupported Amazon Polly action: "' + request.polly_action + '".') + + return actions[request.polly_action](request) + + def _node_request_handler(self, request): + """The callback function for processing service request. + + It never raises. If anything unexpected happens, it will return a PollyResponse with details of the exception. + + :param request: an instance of PollyRequest + :return: a PollyResponse + """ + rospy.loginfo('Amazon Polly Request: {}'.format(request)) + + try: + response = self._dispatch(request) + rospy.loginfo('will return {}'.format(response)) + return PollyResponse(result=response) + except Exception as e: + current_dir = os.path.dirname(os.path.abspath(__file__)) + exc_type = sys.exc_info()[0] + + # not using `issubclass(exc_type, ConnectionError)` for the condition below because some versions + # of urllib3 raises exception when doing `from requests.exceptions import ConnectionError` + error_ogg_filename = 'connerror.ogg' if 'ConnectionError' in exc_type.__name__ else 'error.ogg' + + error_details = { + 'Audio File': os.path.join(current_dir, 'data', error_ogg_filename), + 'Audio Type': 'ogg', + 'Exception': { + 'Type': str(exc_type), + 'Module': exc_type.__module__, + 'Name': exc_type.__name__, + 'Value': str(e), + }, + 'Traceback': traceback.format_exc() + } + + error_str = json.dumps(error_details) + rospy.logerr(error_str) + return PollyResponse(result=error_str) + + def synthesize(self, **kws): + """Call this method if you want to use polly but don't want to start a node. + + :param kws: input as defined in Polly.srv + :return: a string in JSON form with detailed information, success or failure + """ + req = PollyRequest(polly_action='SynthesizeSpeech', **kws) + return self._node_request_handler(req) + + def start(self, node_name='polly_node', service_name='polly'): + """The entry point of a ROS service node. + + Details of the service API can be found in Polly.srv. + + :param node_name: name of ROS node + :param service_name: name of ROS service + :return: it doesn't return + """ + rospy.init_node(node_name) + + service = rospy.Service(service_name, Polly, self._node_request_handler) + + rospy.loginfo('polly running: {}'.format(service.uri)) + + rospy.spin() + + +def main(): + usage = '''usage: %prog [options] + ''' + + parser = OptionParser(usage) + + parser.add_option("-n", "--node-name", dest="node_name", default='polly_node', + help="name of the ROS node", + metavar="NODE_NAME") + parser.add_option("-s", "--service-name", dest="service_name", default='polly', + help="name of the ROS service", + metavar="SERVICE_NAME") + + (options, args) = parser.parse_args() + + node_name = options.node_name + service_name = options.service_name + + AmazonPolly().start(node_name=node_name, service_name=service_name) + + +if __name__ == "__main__": + main() diff --git a/tts/src/tts/data/connerror.ogg b/tts/src/tts/data/connerror.ogg new file mode 100644 index 0000000..7414355 Binary files /dev/null and b/tts/src/tts/data/connerror.ogg differ diff --git a/tts/src/tts/data/error.ogg b/tts/src/tts/data/error.ogg new file mode 100644 index 0000000..4cf247d Binary files /dev/null and b/tts/src/tts/data/error.ogg differ diff --git a/tts/src/tts/data/models/polly/2016-06-10/examples-1.json b/tts/src/tts/data/models/polly/2016-06-10/examples-1.json new file mode 100644 index 0000000..38205db --- /dev/null +++ b/tts/src/tts/data/models/polly/2016-06-10/examples-1.json @@ -0,0 +1,171 @@ +{ + "version": "1.0", + "examples": { + "DeleteLexicon": [ + { + "input": { + "Name": "example" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes a specified pronunciation lexicon stored in an AWS Region.", + "id": "to-delete-a-lexicon-1481922498332", + "title": "To delete a lexicon" + } + ], + "DescribeVoices": [ + { + "input": { + "LanguageCode": "en-GB" + }, + "output": { + "Voices": [ + { + "Gender": "Female", + "Id": "Emma", + "LanguageCode": "en-GB", + "LanguageName": "British English", + "Name": "Emma" + }, + { + "Gender": "Male", + "Id": "Brian", + "LanguageCode": "en-GB", + "LanguageName": "British English", + "Name": "Brian" + }, + { + "Gender": "Female", + "Id": "Amy", + "LanguageCode": "en-GB", + "LanguageName": "British English", + "Name": "Amy" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns the list of voices that are available for use when requesting speech synthesis. Displayed languages are those within the specified language code. If no language code is specified, voices for all available languages are displayed.", + "id": "to-describe-available-voices-1482180557753", + "title": "To describe available voices" + } + ], + "GetLexicon": [ + { + "input": { + "Name": "" + }, + "output": { + "Lexicon": { + "Content": "\r\n\r\n \r\n W3C\r\n World Wide Web Consortium\r\n \r\n", + "Name": "example" + }, + "LexiconAttributes": { + "Alphabet": "ipa", + "LanguageCode": "en-US", + "LastModified": 1478542980.117, + "LexemesCount": 1, + "LexiconArn": "arn:aws:polly:us-east-1:123456789012:lexicon/example", + "Size": 503 + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns the content of the specified pronunciation lexicon stored in an AWS Region.", + "id": "to-retrieve-a-lexicon-1481912870836", + "title": "To retrieve a lexicon" + } + ], + "ListLexicons": [ + { + "input": { + }, + "output": { + "Lexicons": [ + { + "Attributes": { + "Alphabet": "ipa", + "LanguageCode": "en-US", + "LastModified": 1478542980.117, + "LexemesCount": 1, + "LexiconArn": "arn:aws:polly:us-east-1:123456789012:lexicon/example", + "Size": 503 + }, + "Name": "example" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a list of pronunciation lexicons stored in an AWS Region.", + "id": "to-list-all-lexicons-in-a-region-1481842106487", + "title": "To list all lexicons in a region" + } + ], + "PutLexicon": [ + { + "input": { + "Content": "file://example.pls", + "Name": "W3C" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Stores a pronunciation lexicon in an AWS Region.", + "id": "to-save-a-lexicon-1482272584088", + "title": "To save a lexicon" + } + ], + "SynthesizeSpeech": [ + { + "input": { + "LexiconNames": [ + "example" + ], + "OutputFormat": "mp3", + "SampleRate": "8000", + "Text": "All Gaul is divided into three parts", + "TextType": "text", + "VoiceId": "Joanna" + }, + "output": { + "AudioStream": "TEXT", + "ContentType": "audio/mpeg", + "RequestCharacters": 37 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Synthesizes plain text or SSML into a file of human-like speech.", + "id": "to-synthesize-speech-1482186064046", + "title": "To synthesize speech" + } + ] + } +} diff --git a/tts/src/tts/data/models/polly/2016-06-10/paginators-1.json b/tts/src/tts/data/models/polly/2016-06-10/paginators-1.json new file mode 100644 index 0000000..c24ff03 --- /dev/null +++ b/tts/src/tts/data/models/polly/2016-06-10/paginators-1.json @@ -0,0 +1,9 @@ +{ + "pagination": { + "DescribeVoices": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Voices" + } + } +} diff --git a/tts/src/tts/data/models/polly/2016-06-10/service-2.json b/tts/src/tts/data/models/polly/2016-06-10/service-2.json new file mode 100644 index 0000000..bfd6491 --- /dev/null +++ b/tts/src/tts/data/models/polly/2016-06-10/service-2.json @@ -0,0 +1,1022 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2016-06-10", + "endpointPrefix":"polly", + "protocol":"rest-json", + "serviceFullName":"Amazon Polly", + "serviceId":"Polly", + "signatureVersion":"v4", + "uid":"polly-2016-06-10" + }, + "operations":{ + "DeleteLexicon":{ + "name":"DeleteLexicon", + "http":{ + "method":"DELETE", + "requestUri":"/v1/lexicons/{LexiconName}", + "responseCode":200 + }, + "input":{"shape":"DeleteLexiconInput"}, + "output":{"shape":"DeleteLexiconOutput"}, + "errors":[ + {"shape":"LexiconNotFoundException"}, + {"shape":"ServiceFailureException"} + ], + "documentation":"

Deletes the specified pronunciation lexicon stored in an AWS Region. A lexicon which has been deleted is not available for speech synthesis, nor is it possible to retrieve it using either the GetLexicon or ListLexicon APIs.

For more information, see Managing Lexicons.

" + }, + "DescribeVoices":{ + "name":"DescribeVoices", + "http":{ + "method":"GET", + "requestUri":"/v1/voices", + "responseCode":200 + }, + "input":{"shape":"DescribeVoicesInput"}, + "output":{"shape":"DescribeVoicesOutput"}, + "errors":[ + {"shape":"InvalidNextTokenException"}, + {"shape":"ServiceFailureException"} + ], + "documentation":"

Returns the list of voices that are available for use when requesting speech synthesis. Each voice speaks a specified language, is either male or female, and is identified by an ID, which is the ASCII version of the voice name.

When synthesizing speech ( SynthesizeSpeech ), you provide the voice ID for the voice you want from the list of voices returned by DescribeVoices.

For example, you want your news reader application to read news in a specific language, but giving a user the option to choose the voice. Using the DescribeVoices operation you can provide the user with a list of available voices to select from.

You can optionally specify a language code to filter the available voices. For example, if you specify en-US, the operation returns a list of all available US English voices.

This operation requires permissions to perform the polly:DescribeVoices action.

" + }, + "GetLexicon":{ + "name":"GetLexicon", + "http":{ + "method":"GET", + "requestUri":"/v1/lexicons/{LexiconName}", + "responseCode":200 + }, + "input":{"shape":"GetLexiconInput"}, + "output":{"shape":"GetLexiconOutput"}, + "errors":[ + {"shape":"LexiconNotFoundException"}, + {"shape":"ServiceFailureException"} + ], + "documentation":"

Returns the content of the specified pronunciation lexicon stored in an AWS Region. For more information, see Managing Lexicons.

" + }, + "GetSpeechSynthesisTask":{ + "name":"GetSpeechSynthesisTask", + "http":{ + "method":"GET", + "requestUri":"/v1/synthesisTasks/{TaskId}", + "responseCode":200 + }, + "input":{"shape":"GetSpeechSynthesisTaskInput"}, + "output":{"shape":"GetSpeechSynthesisTaskOutput"}, + "errors":[ + {"shape":"InvalidTaskIdException"}, + {"shape":"ServiceFailureException"}, + {"shape":"SynthesisTaskNotFoundException"} + ], + "documentation":"

Retrieves a specific SpeechSynthesisTask object based on its TaskID. This object contains information about the given speech synthesis task, including the status of the task, and a link to the S3 bucket containing the output of the task.

" + }, + "ListLexicons":{ + "name":"ListLexicons", + "http":{ + "method":"GET", + "requestUri":"/v1/lexicons", + "responseCode":200 + }, + "input":{"shape":"ListLexiconsInput"}, + "output":{"shape":"ListLexiconsOutput"}, + "errors":[ + {"shape":"InvalidNextTokenException"}, + {"shape":"ServiceFailureException"} + ], + "documentation":"

Returns a list of pronunciation lexicons stored in an AWS Region. For more information, see Managing Lexicons.

" + }, + "ListSpeechSynthesisTasks":{ + "name":"ListSpeechSynthesisTasks", + "http":{ + "method":"GET", + "requestUri":"/v1/synthesisTasks", + "responseCode":200 + }, + "input":{"shape":"ListSpeechSynthesisTasksInput"}, + "output":{"shape":"ListSpeechSynthesisTasksOutput"}, + "errors":[ + {"shape":"InvalidNextTokenException"}, + {"shape":"ServiceFailureException"} + ], + "documentation":"

Returns a list of SpeechSynthesisTask objects ordered by their creation date. This operation can filter the tasks by their status, for example, allowing users to list only tasks that are completed.

" + }, + "PutLexicon":{ + "name":"PutLexicon", + "http":{ + "method":"PUT", + "requestUri":"/v1/lexicons/{LexiconName}", + "responseCode":200 + }, + "input":{"shape":"PutLexiconInput"}, + "output":{"shape":"PutLexiconOutput"}, + "errors":[ + {"shape":"InvalidLexiconException"}, + {"shape":"UnsupportedPlsAlphabetException"}, + {"shape":"UnsupportedPlsLanguageException"}, + {"shape":"LexiconSizeExceededException"}, + {"shape":"MaxLexemeLengthExceededException"}, + {"shape":"MaxLexiconsNumberExceededException"}, + {"shape":"ServiceFailureException"} + ], + "documentation":"

Stores a pronunciation lexicon in an AWS Region. If a lexicon with the same name already exists in the region, it is overwritten by the new lexicon. Lexicon operations have eventual consistency, therefore, it might take some time before the lexicon is available to the SynthesizeSpeech operation.

For more information, see Managing Lexicons.

" + }, + "StartSpeechSynthesisTask":{ + "name":"StartSpeechSynthesisTask", + "http":{ + "method":"POST", + "requestUri":"/v1/synthesisTasks", + "responseCode":200 + }, + "input":{"shape":"StartSpeechSynthesisTaskInput"}, + "output":{"shape":"StartSpeechSynthesisTaskOutput"}, + "errors":[ + {"shape":"TextLengthExceededException"}, + {"shape":"InvalidS3BucketException"}, + {"shape":"InvalidS3KeyException"}, + {"shape":"InvalidSampleRateException"}, + {"shape":"InvalidSnsTopicArnException"}, + {"shape":"InvalidSsmlException"}, + {"shape":"LexiconNotFoundException"}, + {"shape":"ServiceFailureException"}, + {"shape":"MarksNotSupportedForFormatException"}, + {"shape":"SsmlMarksNotSupportedForTextTypeException"}, + {"shape":"LanguageNotSupportedException"} + ], + "documentation":"

Allows the creation of an asynchronous synthesis task, by starting a new SpeechSynthesisTask. This operation requires all the standard information needed for speech synthesis, plus the name of an Amazon S3 bucket for the service to store the output of the synthesis task and two optional parameters (OutputS3KeyPrefix and SnsTopicArn). Once the synthesis task is created, this operation will return a SpeechSynthesisTask object, which will include an identifier of this task as well as the current status.

" + }, + "SynthesizeSpeech":{ + "name":"SynthesizeSpeech", + "http":{ + "method":"POST", + "requestUri":"/v1/speech", + "responseCode":200 + }, + "input":{"shape":"SynthesizeSpeechInput"}, + "output":{"shape":"SynthesizeSpeechOutput"}, + "errors":[ + {"shape":"TextLengthExceededException"}, + {"shape":"InvalidSampleRateException"}, + {"shape":"InvalidSsmlException"}, + {"shape":"LexiconNotFoundException"}, + {"shape":"ServiceFailureException"}, + {"shape":"MarksNotSupportedForFormatException"}, + {"shape":"SsmlMarksNotSupportedForTextTypeException"}, + {"shape":"LanguageNotSupportedException"} + ], + "documentation":"

Synthesizes UTF-8 input, plain text or SSML, to a stream of bytes. SSML input must be valid, well-formed SSML. Some alphabets might not be available with all the voices (for example, Cyrillic might not be read at all by English voices) unless phoneme mapping is used. For more information, see How it Works.

" + } + }, + "shapes":{ + "Alphabet":{"type":"string"}, + "AudioStream":{ + "type":"blob", + "streaming":true + }, + "ContentType":{"type":"string"}, + "DateTime":{"type":"timestamp"}, + "DeleteLexiconInput":{ + "type":"structure", + "required":["Name"], + "members":{ + "Name":{ + "shape":"LexiconName", + "documentation":"

The name of the lexicon to delete. Must be an existing lexicon in the region.

", + "location":"uri", + "locationName":"LexiconName" + } + } + }, + "DeleteLexiconOutput":{ + "type":"structure", + "members":{ + } + }, + "DescribeVoicesInput":{ + "type":"structure", + "members":{ + "LanguageCode":{ + "shape":"LanguageCode", + "documentation":"

The language identification tag (ISO 639 code for the language name-ISO 3166 country code) for filtering the list of voices returned. If you don't specify this optional parameter, all available voices are returned.

", + "location":"querystring", + "locationName":"LanguageCode" + }, + "IncludeAdditionalLanguageCodes":{ + "shape":"IncludeAdditionalLanguageCodes", + "documentation":"

Boolean value indicating whether to return any bilingual voices that use the specified language as an additional language. For instance, if you request all languages that use US English (es-US), and there is an Italian voice that speaks both Italian (it-IT) and US English, that voice will be included if you specify yes but not if you specify no.

", + "location":"querystring", + "locationName":"IncludeAdditionalLanguageCodes" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

An opaque pagination token returned from the previous DescribeVoices operation. If present, this indicates where to continue the listing.

", + "location":"querystring", + "locationName":"NextToken" + } + } + }, + "DescribeVoicesOutput":{ + "type":"structure", + "members":{ + "Voices":{ + "shape":"VoiceList", + "documentation":"

A list of voices with their properties.

" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

The pagination token to use in the next request to continue the listing of voices. NextToken is returned only if the response is truncated.

" + } + } + }, + "ErrorMessage":{"type":"string"}, + "Gender":{ + "type":"string", + "enum":[ + "Female", + "Male" + ] + }, + "GetLexiconInput":{ + "type":"structure", + "required":["Name"], + "members":{ + "Name":{ + "shape":"LexiconName", + "documentation":"

Name of the lexicon.

", + "location":"uri", + "locationName":"LexiconName" + } + } + }, + "GetLexiconOutput":{ + "type":"structure", + "members":{ + "Lexicon":{ + "shape":"Lexicon", + "documentation":"

Lexicon object that provides name and the string content of the lexicon.

" + }, + "LexiconAttributes":{ + "shape":"LexiconAttributes", + "documentation":"

Metadata of the lexicon, including phonetic alphabetic used, language code, lexicon ARN, number of lexemes defined in the lexicon, and size of lexicon in bytes.

" + } + } + }, + "GetSpeechSynthesisTaskInput":{ + "type":"structure", + "required":["TaskId"], + "members":{ + "TaskId":{ + "shape":"TaskId", + "documentation":"

The Amazon Polly generated identifier for a speech synthesis task.

", + "location":"uri", + "locationName":"TaskId" + } + } + }, + "GetSpeechSynthesisTaskOutput":{ + "type":"structure", + "members":{ + "SynthesisTask":{ + "shape":"SynthesisTask", + "documentation":"

SynthesisTask object that provides information from the requested task, including output format, creation time, task status, and so on.

" + } + } + }, + "IncludeAdditionalLanguageCodes":{"type":"boolean"}, + "InvalidLexiconException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

Amazon Polly can't find the specified lexicon. Verify that the lexicon's name is spelled correctly, and then try again.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidNextTokenException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The NextToken is invalid. Verify that it's spelled correctly, and then try again.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidS3BucketException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The provided Amazon S3 bucket name is invalid. Please check your input with S3 bucket naming requirements and try again.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidS3KeyException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The provided Amazon S3 key prefix is invalid. Please provide a valid S3 object key name.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidSampleRateException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The specified sample rate is not valid.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidSnsTopicArnException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The provided SNS topic ARN is invalid. Please provide a valid SNS topic ARN and try again.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidSsmlException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The SSML you provided is invalid. Verify the SSML syntax, spelling of tags and values, and then try again.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidTaskIdException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The provided Task ID is not valid. Please provide a valid Task ID and try again.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "LanguageCode":{ + "type":"string", + "enum":[ + "cmn-CN", + "cy-GB", + "da-DK", + "de-DE", + "en-AU", + "en-GB", + "en-GB-WLS", + "en-IN", + "en-US", + "es-ES", + "es-US", + "fr-CA", + "fr-FR", + "is-IS", + "it-IT", + "ja-JP", + "hi-IN", + "ko-KR", + "nb-NO", + "nl-NL", + "pl-PL", + "pt-BR", + "pt-PT", + "ro-RO", + "ru-RU", + "sv-SE", + "tr-TR" + ] + }, + "LanguageCodeList":{ + "type":"list", + "member":{"shape":"LanguageCode"} + }, + "LanguageName":{"type":"string"}, + "LanguageNotSupportedException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The language specified is not currently supported by Amazon Polly in this capacity.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "LastModified":{"type":"timestamp"}, + "LexemesCount":{"type":"integer"}, + "Lexicon":{ + "type":"structure", + "members":{ + "Content":{ + "shape":"LexiconContent", + "documentation":"

Lexicon content in string format. The content of a lexicon must be in PLS format.

" + }, + "Name":{ + "shape":"LexiconName", + "documentation":"

Name of the lexicon.

" + } + }, + "documentation":"

Provides lexicon name and lexicon content in string format. For more information, see Pronunciation Lexicon Specification (PLS) Version 1.0.

" + }, + "LexiconArn":{"type":"string"}, + "LexiconAttributes":{ + "type":"structure", + "members":{ + "Alphabet":{ + "shape":"Alphabet", + "documentation":"

Phonetic alphabet used in the lexicon. Valid values are ipa and x-sampa.

" + }, + "LanguageCode":{ + "shape":"LanguageCode", + "documentation":"

Language code that the lexicon applies to. A lexicon with a language code such as \"en\" would be applied to all English languages (en-GB, en-US, en-AUS, en-WLS, and so on.

" + }, + "LastModified":{ + "shape":"LastModified", + "documentation":"

Date lexicon was last modified (a timestamp value).

" + }, + "LexiconArn":{ + "shape":"LexiconArn", + "documentation":"

Amazon Resource Name (ARN) of the lexicon.

" + }, + "LexemesCount":{ + "shape":"LexemesCount", + "documentation":"

Number of lexemes in the lexicon.

" + }, + "Size":{ + "shape":"Size", + "documentation":"

Total size of the lexicon, in characters.

" + } + }, + "documentation":"

Contains metadata describing the lexicon such as the number of lexemes, language code, and so on. For more information, see Managing Lexicons.

" + }, + "LexiconContent":{"type":"string"}, + "LexiconDescription":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"LexiconName", + "documentation":"

Name of the lexicon.

" + }, + "Attributes":{ + "shape":"LexiconAttributes", + "documentation":"

Provides lexicon metadata.

" + } + }, + "documentation":"

Describes the content of the lexicon.

" + }, + "LexiconDescriptionList":{ + "type":"list", + "member":{"shape":"LexiconDescription"} + }, + "LexiconName":{ + "type":"string", + "pattern":"[0-9A-Za-z]{1,20}", + "sensitive":true + }, + "LexiconNameList":{ + "type":"list", + "member":{"shape":"LexiconName"}, + "max":5 + }, + "LexiconNotFoundException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

Amazon Polly can't find the specified lexicon. This could be caused by a lexicon that is missing, its name is misspelled or specifying a lexicon that is in a different region.

Verify that the lexicon exists, is in the region (see ListLexicons) and that you spelled its name is spelled correctly. Then try again.

", + "error":{"httpStatusCode":404}, + "exception":true + }, + "LexiconSizeExceededException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The maximum size of the specified lexicon would be exceeded by this operation.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ListLexiconsInput":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

An opaque pagination token returned from previous ListLexicons operation. If present, indicates where to continue the list of lexicons.

", + "location":"querystring", + "locationName":"NextToken" + } + } + }, + "ListLexiconsOutput":{ + "type":"structure", + "members":{ + "Lexicons":{ + "shape":"LexiconDescriptionList", + "documentation":"

A list of lexicon names and attributes.

" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

The pagination token to use in the next request to continue the listing of lexicons. NextToken is returned only if the response is truncated.

" + } + } + }, + "ListSpeechSynthesisTasksInput":{ + "type":"structure", + "members":{ + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

Maximum number of speech synthesis tasks returned in a List operation.

", + "location":"querystring", + "locationName":"MaxResults" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

The pagination token to use in the next request to continue the listing of speech synthesis tasks.

", + "location":"querystring", + "locationName":"NextToken" + }, + "Status":{ + "shape":"TaskStatus", + "documentation":"

Status of the speech synthesis tasks returned in a List operation

", + "location":"querystring", + "locationName":"Status" + } + } + }, + "ListSpeechSynthesisTasksOutput":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

An opaque pagination token returned from the previous List operation in this request. If present, this indicates where to continue the listing.

" + }, + "SynthesisTasks":{ + "shape":"SynthesisTasks", + "documentation":"

List of SynthesisTask objects that provides information from the specified task in the list request, including output format, creation time, task status, and so on.

" + } + } + }, + "MarksNotSupportedForFormatException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

Speech marks are not supported for the OutputFormat selected. Speech marks are only available for content in json format.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "MaxLexemeLengthExceededException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The maximum size of the lexeme would be exceeded by this operation.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "MaxLexiconsNumberExceededException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The maximum number of lexicons would be exceeded by this operation.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "MaxResults":{ + "type":"integer", + "max":100, + "min":1 + }, + "NextToken":{"type":"string"}, + "OutputFormat":{ + "type":"string", + "enum":[ + "json", + "mp3", + "ogg_vorbis", + "pcm" + ] + }, + "OutputS3BucketName":{ + "type":"string", + "pattern":"^[a-z0-9][\\.\\-a-z0-9]{1,61}[a-z0-9]$" + }, + "OutputS3KeyPrefix":{ + "type":"string", + "pattern":"^[0-9a-zA-Z\\/\\!\\-_\\.\\*\\'\\(\\)]{0,800}$" + }, + "OutputUri":{"type":"string"}, + "PutLexiconInput":{ + "type":"structure", + "required":[ + "Name", + "Content" + ], + "members":{ + "Name":{ + "shape":"LexiconName", + "documentation":"

Name of the lexicon. The name must follow the regular express format [0-9A-Za-z]{1,20}. That is, the name is a case-sensitive alphanumeric string up to 20 characters long.

", + "location":"uri", + "locationName":"LexiconName" + }, + "Content":{ + "shape":"LexiconContent", + "documentation":"

Content of the PLS lexicon as string data.

" + } + } + }, + "PutLexiconOutput":{ + "type":"structure", + "members":{ + } + }, + "RequestCharacters":{"type":"integer"}, + "SampleRate":{"type":"string"}, + "ServiceFailureException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

An unknown condition has caused a service failure.

", + "error":{"httpStatusCode":500}, + "exception":true, + "fault":true + }, + "Size":{"type":"integer"}, + "SnsTopicArn":{ + "type":"string", + "pattern":"^arn:aws(-(cn|iso(-b)?|us-gov))?:sns:.*:\\w{12}:.+$" + }, + "SpeechMarkType":{ + "type":"string", + "enum":[ + "sentence", + "ssml", + "viseme", + "word" + ] + }, + "SpeechMarkTypeList":{ + "type":"list", + "member":{"shape":"SpeechMarkType"}, + "max":4 + }, + "SsmlMarksNotSupportedForTextTypeException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

SSML speech marks are not supported for plain text-type input.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "StartSpeechSynthesisTaskInput":{ + "type":"structure", + "required":[ + "OutputFormat", + "OutputS3BucketName", + "Text", + "VoiceId" + ], + "members":{ + "LexiconNames":{ + "shape":"LexiconNameList", + "documentation":"

List of one or more pronunciation lexicon names you want the service to apply during synthesis. Lexicons are applied only if the language of the lexicon is the same as the language of the voice.

" + }, + "OutputFormat":{ + "shape":"OutputFormat", + "documentation":"

The format in which the returned output will be encoded. For audio stream, this will be mp3, ogg_vorbis, or pcm. For speech marks, this will be json.

" + }, + "OutputS3BucketName":{ + "shape":"OutputS3BucketName", + "documentation":"

Amazon S3 bucket name to which the output file will be saved.

" + }, + "OutputS3KeyPrefix":{ + "shape":"OutputS3KeyPrefix", + "documentation":"

The Amazon S3 key prefix for the output speech file.

" + }, + "SampleRate":{ + "shape":"SampleRate", + "documentation":"

The audio frequency specified in Hz.

The valid values for mp3 and ogg_vorbis are \"8000\", \"16000\", and \"22050\". The default value is \"22050\".

Valid values for pcm are \"8000\" and \"16000\" The default value is \"16000\".

" + }, + "SnsTopicArn":{ + "shape":"SnsTopicArn", + "documentation":"

ARN for the SNS topic optionally used for providing status notification for a speech synthesis task.

" + }, + "SpeechMarkTypes":{ + "shape":"SpeechMarkTypeList", + "documentation":"

The type of speech marks returned for the input text.

" + }, + "Text":{ + "shape":"Text", + "documentation":"

The input text to synthesize. If you specify ssml as the TextType, follow the SSML format for the input text.

" + }, + "TextType":{ + "shape":"TextType", + "documentation":"

Specifies whether the input text is plain text or SSML. The default value is plain text.

" + }, + "VoiceId":{ + "shape":"VoiceId", + "documentation":"

Voice ID to use for the synthesis.

" + }, + "LanguageCode":{ + "shape":"LanguageCode", + "documentation":"

Optional language code for the Speech Synthesis request. This is only necessary if using a bilingual voice, such as Aditi, which can be used for either Indian English (en-IN) or Hindi (hi-IN).

If a bilingual voice is used and no language code is specified, Amazon Polly will use the default language of the bilingual voice. The default language for any voice is the one returned by the DescribeVoices operation for the LanguageCode parameter. For example, if no language code is specified, Aditi will use Indian English rather than Hindi.

" + } + } + }, + "StartSpeechSynthesisTaskOutput":{ + "type":"structure", + "members":{ + "SynthesisTask":{ + "shape":"SynthesisTask", + "documentation":"

SynthesisTask object that provides information and attributes about a newly submitted speech synthesis task.

" + } + } + }, + "SynthesisTask":{ + "type":"structure", + "members":{ + "TaskId":{ + "shape":"TaskId", + "documentation":"

The Amazon Polly generated identifier for a speech synthesis task.

" + }, + "TaskStatus":{ + "shape":"TaskStatus", + "documentation":"

Current status of the individual speech synthesis task.

" + }, + "TaskStatusReason":{ + "shape":"TaskStatusReason", + "documentation":"

Reason for the current status of a specific speech synthesis task, including errors if the task has failed.

" + }, + "OutputUri":{ + "shape":"OutputUri", + "documentation":"

Pathway for the output speech file.

" + }, + "CreationTime":{ + "shape":"DateTime", + "documentation":"

Timestamp for the time the synthesis task was started.

" + }, + "RequestCharacters":{ + "shape":"RequestCharacters", + "documentation":"

Number of billable characters synthesized.

" + }, + "SnsTopicArn":{ + "shape":"SnsTopicArn", + "documentation":"

ARN for the SNS topic optionally used for providing status notification for a speech synthesis task.

" + }, + "LexiconNames":{ + "shape":"LexiconNameList", + "documentation":"

List of one or more pronunciation lexicon names you want the service to apply during synthesis. Lexicons are applied only if the language of the lexicon is the same as the language of the voice.

" + }, + "OutputFormat":{ + "shape":"OutputFormat", + "documentation":"

The format in which the returned output will be encoded. For audio stream, this will be mp3, ogg_vorbis, or pcm. For speech marks, this will be json.

" + }, + "SampleRate":{ + "shape":"SampleRate", + "documentation":"

The audio frequency specified in Hz.

The valid values for mp3 and ogg_vorbis are \"8000\", \"16000\", and \"22050\". The default value is \"22050\".

Valid values for pcm are \"8000\" and \"16000\" The default value is \"16000\".

" + }, + "SpeechMarkTypes":{ + "shape":"SpeechMarkTypeList", + "documentation":"

The type of speech marks returned for the input text.

" + }, + "TextType":{ + "shape":"TextType", + "documentation":"

Specifies whether the input text is plain text or SSML. The default value is plain text.

" + }, + "VoiceId":{ + "shape":"VoiceId", + "documentation":"

Voice ID to use for the synthesis.

" + }, + "LanguageCode":{ + "shape":"LanguageCode", + "documentation":"

Optional language code for a synthesis task. This is only necessary if using a bilingual voice, such as Aditi, which can be used for either Indian English (en-IN) or Hindi (hi-IN).

If a bilingual voice is used and no language code is specified, Amazon Polly will use the default language of the bilingual voice. The default language for any voice is the one returned by the DescribeVoices operation for the LanguageCode parameter. For example, if no language code is specified, Aditi will use Indian English rather than Hindi.

" + } + }, + "documentation":"

SynthesisTask object that provides information about a speech synthesis task.

" + }, + "SynthesisTaskNotFoundException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The Speech Synthesis task with requested Task ID cannot be found.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "SynthesisTasks":{ + "type":"list", + "member":{"shape":"SynthesisTask"} + }, + "SynthesizeSpeechInput":{ + "type":"structure", + "required":[ + "OutputFormat", + "Text", + "VoiceId" + ], + "members":{ + "LexiconNames":{ + "shape":"LexiconNameList", + "documentation":"

List of one or more pronunciation lexicon names you want the service to apply during synthesis. Lexicons are applied only if the language of the lexicon is the same as the language of the voice. For information about storing lexicons, see PutLexicon.

" + }, + "OutputFormat":{ + "shape":"OutputFormat", + "documentation":"

The format in which the returned output will be encoded. For audio stream, this will be mp3, ogg_vorbis, or pcm. For speech marks, this will be json.

When pcm is used, the content returned is audio/pcm in a signed 16-bit, 1 channel (mono), little-endian format.

" + }, + "SampleRate":{ + "shape":"SampleRate", + "documentation":"

The audio frequency specified in Hz.

The valid values for mp3 and ogg_vorbis are \"8000\", \"16000\", and \"22050\". The default value is \"22050\".

Valid values for pcm are \"8000\" and \"16000\" The default value is \"16000\".

" + }, + "SpeechMarkTypes":{ + "shape":"SpeechMarkTypeList", + "documentation":"

The type of speech marks returned for the input text.

" + }, + "Text":{ + "shape":"Text", + "documentation":"

Input text to synthesize. If you specify ssml as the TextType, follow the SSML format for the input text.

" + }, + "TextType":{ + "shape":"TextType", + "documentation":"

Specifies whether the input text is plain text or SSML. The default value is plain text. For more information, see Using SSML.

" + }, + "VoiceId":{ + "shape":"VoiceId", + "documentation":"

Voice ID to use for the synthesis. You can get a list of available voice IDs by calling the DescribeVoices operation.

" + }, + "LanguageCode":{ + "shape":"LanguageCode", + "documentation":"

Optional language code for the Synthesize Speech request. This is only necessary if using a bilingual voice, such as Aditi, which can be used for either Indian English (en-IN) or Hindi (hi-IN).

If a bilingual voice is used and no language code is specified, Amazon Polly will use the default language of the bilingual voice. The default language for any voice is the one returned by the DescribeVoices operation for the LanguageCode parameter. For example, if no language code is specified, Aditi will use Indian English rather than Hindi.

" + } + } + }, + "SynthesizeSpeechOutput":{ + "type":"structure", + "members":{ + "AudioStream":{ + "shape":"AudioStream", + "documentation":"

Stream containing the synthesized speech.

" + }, + "ContentType":{ + "shape":"ContentType", + "documentation":"

Specifies the type audio stream. This should reflect the OutputFormat parameter in your request.

", + "location":"header", + "locationName":"Content-Type" + }, + "RequestCharacters":{ + "shape":"RequestCharacters", + "documentation":"

Number of characters synthesized.

", + "location":"header", + "locationName":"x-amzn-RequestCharacters" + } + }, + "payload":"AudioStream" + }, + "TaskId":{ + "type":"string", + "max":128, + "min":1 + }, + "TaskStatus":{ + "type":"string", + "enum":[ + "scheduled", + "inProgress", + "completed", + "failed" + ] + }, + "TaskStatusReason":{"type":"string"}, + "Text":{"type":"string"}, + "TextLengthExceededException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The value of the \"Text\" parameter is longer than the accepted limits. For the SynthesizeSpeech API, the limit for input text is a maximum of 6000 characters total, of which no more than 3000 can be billed characters. For the StartSpeechSynthesisTask API, the maximum is 200,000 characters, of which no more than 100,000 can be billed characters. SSML tags are not counted as billed characters.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "TextType":{ + "type":"string", + "enum":[ + "ssml", + "text" + ] + }, + "UnsupportedPlsAlphabetException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The alphabet specified by the lexicon is not a supported alphabet. Valid values are x-sampa and ipa.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "UnsupportedPlsLanguageException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

The language specified in the lexicon is unsupported. For a list of supported languages, see Lexicon Attributes.

", + "error":{"httpStatusCode":400}, + "exception":true + }, + "Voice":{ + "type":"structure", + "members":{ + "Gender":{ + "shape":"Gender", + "documentation":"

Gender of the voice.

" + }, + "Id":{ + "shape":"VoiceId", + "documentation":"

Amazon Polly assigned voice ID. This is the ID that you specify when calling the SynthesizeSpeech operation.

" + }, + "LanguageCode":{ + "shape":"LanguageCode", + "documentation":"

Language code of the voice.

" + }, + "LanguageName":{ + "shape":"LanguageName", + "documentation":"

Human readable name of the language in English.

" + }, + "Name":{ + "shape":"VoiceName", + "documentation":"

Name of the voice (for example, Salli, Kendra, etc.). This provides a human readable voice name that you might display in your application.

" + }, + "AdditionalLanguageCodes":{ + "shape":"LanguageCodeList", + "documentation":"

Additional codes for languages available for the specified voice in addition to its default language.

For example, the default language for Aditi is Indian English (en-IN) because it was first used for that language. Since Aditi is bilingual and fluent in both Indian English and Hindi, this parameter would show the code hi-IN.

" + } + }, + "documentation":"

Description of the voice.

" + }, + "VoiceId":{ + "type":"string", + "enum":[ + "Geraint", + "Gwyneth", + "Mads", + "Naja", + "Hans", + "Marlene", + "Nicole", + "Russell", + "Amy", + "Brian", + "Emma", + "Raveena", + "Ivy", + "Joanna", + "Joey", + "Justin", + "Kendra", + "Kimberly", + "Matthew", + "Salli", + "Conchita", + "Enrique", + "Miguel", + "Penelope", + "Chantal", + "Celine", + "Lea", + "Mathieu", + "Dora", + "Karl", + "Carla", + "Giorgio", + "Mizuki", + "Liv", + "Lotte", + "Ruben", + "Ewa", + "Jacek", + "Jan", + "Maja", + "Ricardo", + "Vitoria", + "Cristiano", + "Ines", + "Carmen", + "Maxim", + "Tatyana", + "Astrid", + "Filiz", + "Vicki", + "Takumi", + "Seoyeon", + "Aditi", + "Zhiyu" + ] + }, + "VoiceList":{ + "type":"list", + "member":{"shape":"Voice"} + }, + "VoiceName":{"type":"string"} + }, + "documentation":"

Amazon Polly is a web service that makes it easy to synthesize speech from text.

The Amazon Polly service provides API operations for synthesizing high-quality speech from plain text and Speech Synthesis Markup Language (SSML), along with managing pronunciations lexicons that enable you to get the best results for your application domain.

" +} diff --git a/tts/src/tts/synthesizer.py b/tts/src/tts/synthesizer.py new file mode 100755 index 0000000..91b6e07 --- /dev/null +++ b/tts/src/tts/synthesizer.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +import os +import time +import json +import rospy +import hashlib +from optparse import OptionParser +from tts.srv import Synthesizer, SynthesizerResponse + + +class SpeechSynthesizer: + """This class serves as a ROS service node that should be an entry point of a TTS task. + + Although the current implementation uses Amazon Polly as the synthesis engine, it is not hard to let it support + more heterogeneous engines while keeping the API the same. + + In order to support a variety of engines, the SynthesizerRequest was designed with flexibility in mind. It + has two fields: text and metadata. Both are strings. In most cases, a user can ignore the metadata and call + the service with some plain text. If the use case needs any control or engine-specific feature, the extra + information can be put into the JSON-form metadata. This class will use the information when calling the engine. + + The decoupling of the synthesizer and the actual synthesis engine will benefit the users in many ways. + + First, a user will be able to use a unified interface to do the TTS job and have the freedom to use different + engines available with no or very little change from the client side. + + Second, by applying some design patterns, the synthesizer can choose an engine dynamically. For example, a user + may prefer to use Amazon Polly but is also OK with an offline solution when network is not reliable. + + Third, engines can be complicated, thus difficult to use. As an example, Amazon Polly supports dozens of parameters + and is able to accomplish nontrivial synthesis jobs, but majority of the users never need those features. This + class provides a clean interface with two parameters only, so that it is much easier and pleasant to use. If by + any chance the advanced features are required, the user can always leverage the metadata field or even go to the + backend engine directly. + + Also, from an engineering perspective, simple and decoupled modules are easier to maintain. + + This class supports two modes of using polly. It can either call a service node or use AmazonPolly as a library. + + Start the service node:: + + $ rosrun tts synthesizer_node.py # use default configuration + $ rosrun tts synthesizer_node.py -e POLLY_LIBRARY # will not call polly service node + + Call the service:: + + $ rosservice call /synthesizer 'hello' '' + $ rosservice call /synthesizer 'hello' '"{\"text_type\":\"ssml\"}"' + """ + + class PollyViaNode: + def __init__(self, polly_service_name='polly'): + self.service_name = polly_service_name + + def __call__(self, **kwargs): + rospy.loginfo('will call service {}'.format(self.service_name)) + from tts.srv import Polly + rospy.wait_for_service(self.service_name) + polly = rospy.ServiceProxy(self.service_name, Polly) + return polly(polly_action='SynthesizeSpeech', **kwargs) + + class PollyDirect: + def __init__(self): + pass + + def __call__(self, **kwargs): + rospy.loginfo('will import amazonpolly.AmazonPolly') + from tts.amazonpolly import AmazonPolly + node = AmazonPolly() + return node.synthesize(**kwargs) + + ENGINES = { + 'POLLY_SERVICE': PollyViaNode, + 'POLLY_LIBRARY': PollyDirect, + } + + class BadEngineError(NameError): + pass + + def __init__(self, engine='POLLY_SERVICE', polly_service_name='polly'): + if engine not in self.ENGINES: + msg = 'bad engine {} which is not one of {}'.format(engine, ', '.join(SpeechSynthesizer.ENGINES.keys())) + raise SpeechSynthesizer.BadEngineError(msg) + + engine_kwargs = {'polly_service_name': polly_service_name} if engine == 'POLLY_SERVICE' else {} + self.engine = self.ENGINES[engine](**engine_kwargs) + + self.default_text_type = 'text' + self.default_voice_id = 'Joanna' + self.default_output_format = 'ogg_vorbis' + + def _call_engine(self, **kw): + """Call engine to do the job. + + If no output path is found from input, the audio file will be put into /tmp and the file name will have + a prefix of the md5 hash of the text. + + :param kw: what AmazonPolly needs to synthesize + :return: response from AmazonPolly + """ + if 'output_path' not in kw: + tmp_filename = hashlib.md5(kw['text']).hexdigest() + tmp_filepath = os.path.join(os.sep, 'tmp', 'voice_{}_{}'.format(tmp_filename, str(time.time()))) + kw['output_path'] = os.path.abspath(tmp_filepath) + rospy.loginfo('audio will be saved as {}'.format(kw['output_path'])) + + return self.engine(**kw) + + def _parse_request_or_raise(self, request): + """It will raise if request is malformed. + + :param request: an instance of SynthesizerRequest + :return: a dict + """ + md = json.loads(request.metadata) if request.metadata else {} + + md['output_format'] = md.get('output_format', self.default_output_format) + md['voice_id'] = md.get('voice_id', self.default_voice_id) + md['sample_rate'] = md.get('sample_rate', '16000' if md['output_format'].lower() == 'pcm' else '22050') + md['text_type'] = md.get('text_type', self.default_text_type) + md['text'] = request.text + + return md + + def _node_request_handler(self, request): + """The callback function for processing service request. + + It never raises. If anything unexpected happens, it will return a SynthesizerResponse with the exception. + + :param request: an instance of SynthesizerRequest + :return: a SynthesizerResponse + """ + rospy.loginfo(request) + try: + kws = self._parse_request_or_raise(request) + res = self._call_engine(**kws).result + + return SynthesizerResponse(res) + except Exception as e: + return SynthesizerResponse('Exception: {}'.format(e)) + + def start(self, node_name='synthesizer_node', service_name='synthesizer'): + """The entry point of a ROS service node. + + :param node_name: name of ROS node + :param service_name: name of ROS service + :return: it doesn't return + """ + rospy.init_node(node_name) + + service = rospy.Service(service_name, Synthesizer, self._node_request_handler) + + rospy.loginfo('{} running: {}'.format(node_name, service.uri)) + + rospy.spin() + + +def main(): + usage = '''usage: %prog [options] + ''' + + parser = OptionParser(usage) + + parser.add_option("-n", "--node-name", dest="node_name", default='synthesizer_node', + help="name of the ROS node", + metavar="NODE_NAME") + parser.add_option("-s", "--service-name", dest="service_name", default='synthesizer', + help="name of the ROS service", + metavar="SERVICE_NAME") + parser.add_option("-e", "--engine", dest="engine", default='POLLY_SERVICE', + help="name of the synthesis engine", + metavar="ENGINE") + parser.add_option("-p", "--polly-service-name", dest="polly_service_name", default='polly', + help="name of the polly service", + metavar="POLLY_SERVICE_NAME") + + (options, args) = parser.parse_args() + + node_name = options.node_name + service_name = options.service_name + engine = options.engine + polly_service_name = options.polly_service_name + + if engine == 'POLLY_SERVICE': + synthesizer = SpeechSynthesizer(engine=engine, polly_service_name=polly_service_name) + else: + synthesizer = SpeechSynthesizer(engine=engine) + synthesizer.start(node_name=node_name, service_name=service_name) + + +if __name__ == "__main__": + main() diff --git a/tts/srv/Polly.srv b/tts/srv/Polly.srv new file mode 100644 index 0000000..02ac7ad --- /dev/null +++ b/tts/srv/Polly.srv @@ -0,0 +1,22 @@ +string polly_action +string text +string text_type +string language_code +string voice_id +string output_format +string output_path +string sample_rate +string lexicon_content +string lexicon_name +string[] lexicon_names +string[] speech_mark_types +uint32 max_results +string next_token +string sns_topic_arn +string task_id +string task_status +string output_s3_bucket_name +string output_s3_key_prefix +bool include_additional_language_codes +--- +string result diff --git a/tts/srv/Synthesizer.srv b/tts/srv/Synthesizer.srv new file mode 100644 index 0000000..d623bb2 --- /dev/null +++ b/tts/srv/Synthesizer.srv @@ -0,0 +1,4 @@ +string text +string metadata +--- +string result diff --git a/tts/test/integration_tests.test b/tts/test/integration_tests.test new file mode 100644 index 0000000..4f662be --- /dev/null +++ b/tts/test/integration_tests.test @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tts/test/test_integration.py b/tts/test/test_integration.py new file mode 100755 index 0000000..3f798f1 --- /dev/null +++ b/tts/test/test_integration.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +from __future__ import print_function + +import sys +import json +import unittest + +import rospy +import rostest + +from tts.srv import Polly +from tts.srv import PollyResponse +from tts.srv import Synthesizer +from tts.srv import SynthesizerResponse + +# import tts which is a relay package, otherwise things don't work +# +# devel/lib/python2.7/dist-packages/ +# +-- tts +# | +-- __init__.py +# +-- ... +# +# per http://docs.ros.org/api/catkin/html/user_guide/setup_dot_py.html: +# +# A relay package is a folder with an __init__.py folder and nothing else. +# Importing this folder in python will execute the contents of __init__.py, +# which will in turn import the original python modules in the folder in +# the sourcespace using the python exec() function. + + +PKG = 'tts' +NAME = 'amazonpolly' + + +class TestPlainText(unittest.TestCase): + + def test_plain_text_to_wav_via_polly_node(self): + rospy.wait_for_service('polly') + polly = rospy.ServiceProxy('polly', Polly) + + test_text = 'Mary has a little lamb, little lamb, little lamb.' + res = polly(polly_action='SynthesizeSpeech', text=test_text) + self.assertIsNotNone(res) + self.assertTrue(type(res) is PollyResponse) + + r = json.loads(res.result) + self.assertIn('Audio Type', r, 'result should contain audio type') + self.assertIn('Audio File', r, 'result should contain file path') + self.assertIn('Amazon Polly Response Metadata', r, 'result should contain metadata') + + audio_type = r['Audio Type'] + audio_file = r['Audio File'] + md = r['Amazon Polly Response Metadata'] + self.assertTrue("'HTTPStatusCode': 200," in md) + self.assertEqual('audio/ogg', audio_type) + self.assertTrue(audio_file.endswith('.ogg')) + + import subprocess + o = subprocess.check_output(['file', audio_file], stderr=subprocess.STDOUT) + import re + m = re.search(r'.*Ogg data, Vorbis audi.*', o, flags=re.MULTILINE) + self.assertIsNotNone(m) + + def test_plain_text_using_polly_class(self): + from tts.amazonpolly import AmazonPolly + polly = AmazonPolly() + test_text = 'Mary has a little lamb, little lamb, little lamb.' + res = polly.synthesize(text=test_text) + self.assertIsNotNone(res) + self.assertTrue(type(res) is PollyResponse) + + r = json.loads(res.result) + self.assertIn('Audio Type', r, 'result should contain audio type') + self.assertIn('Audio File', r, 'result should contain file path') + self.assertIn('Amazon Polly Response Metadata', r, 'result should contain metadata') + + audio_type = r['Audio Type'] + audio_file = r['Audio File'] + md = r['Amazon Polly Response Metadata'] + self.assertTrue("'HTTPStatusCode': 200," in md) + self.assertEqual('audio/ogg', audio_type) + self.assertTrue(audio_file.endswith('.ogg')) + + import subprocess + o = subprocess.check_output(['file', audio_file], stderr=subprocess.STDOUT) + import re + m = re.search(r'.*Ogg data, Vorbis audi.*', o, flags=re.MULTILINE) + self.assertIsNotNone(m) + + def test_plain_text_via_synthesizer_node(self): + rospy.wait_for_service('synthesizer') + speech_synthesizer = rospy.ServiceProxy('synthesizer', Synthesizer) + + text = 'Mary has a little lamb, little lamb, little lamb.' + res = speech_synthesizer(text=text) + self.assertIsNotNone(res) + self.assertTrue(type(res) is SynthesizerResponse) + + r = json.loads(res.result) + self.assertIn('Audio Type', r, 'result should contain audio type') + self.assertIn('Audio File', r, 'result should contain file path') + self.assertIn('Amazon Polly Response Metadata', r, 'result should contain metadata') + + audio_type = r['Audio Type'] + audio_file = r['Audio File'] + md = r['Amazon Polly Response Metadata'] + self.assertTrue("'HTTPStatusCode': 200," in md) + self.assertEqual('audio/ogg', audio_type) + self.assertTrue(audio_file.endswith('.ogg')) + + import subprocess + o = subprocess.check_output(['file', audio_file], stderr=subprocess.STDOUT) + import re + m = re.search(r'.*Ogg data, Vorbis audi.*', o, flags=re.MULTILINE) + self.assertIsNotNone(m) + + def test_plain_text_to_mp3_via_polly_node(self): + rospy.wait_for_service('polly') + polly = rospy.ServiceProxy('polly', Polly) + + test_text = 'Mary has a little lamb, little lamb, little lamb.' + res = polly(polly_action='SynthesizeSpeech', text=test_text, output_format='mp3') + self.assertIsNotNone(res) + self.assertTrue(type(res) is PollyResponse) + + r = json.loads(res.result) + self.assertIn('Audio Type', r, 'result should contain audio type') + self.assertIn('Audio File', r, 'result should contain file path') + self.assertIn('Amazon Polly Response Metadata', r, 'result should contain metadata') + + audio_type = r['Audio Type'] + audio_file = r['Audio File'] + md = r['Amazon Polly Response Metadata'] + self.assertTrue("'HTTPStatusCode': 200," in md) + self.assertEqual('audio/mpeg', audio_type) + self.assertTrue(audio_file.endswith('.mp3')) + + import subprocess + o = subprocess.check_output(['file', audio_file], stderr=subprocess.STDOUT) + import re + m = re.search(r'.*MPEG.*layer III.*', o, flags=re.MULTILINE) + self.assertIsNotNone(m) + + def test_simple_ssml_via_polly_node(self): + rospy.wait_for_service('polly') + polly = rospy.ServiceProxy('polly', Polly) + + text = 'Mary has a little lamb, little lamb, little lamb.' + res = polly(polly_action='SynthesizeSpeech', text=text, text_type='ssml') + self.assertIsNotNone(res) + self.assertTrue(type(res) is PollyResponse) + + r = json.loads(res.result) + self.assertIn('Audio Type', r, 'result should contain audio type') + self.assertIn('Audio File', r, 'result should contain file path') + self.assertIn('Amazon Polly Response Metadata', r, 'result should contain metadata') + + audio_type = r['Audio Type'] + audio_file = r['Audio File'] + md = r['Amazon Polly Response Metadata'] + self.assertTrue("'HTTPStatusCode': 200," in md) + self.assertEqual('audio/ogg', audio_type) + self.assertTrue(audio_file.endswith('.ogg')) + + import subprocess + o = subprocess.check_output(['file', audio_file], stderr=subprocess.STDOUT) + import re + m = re.search(r'.*Ogg data, Vorbis audi.*', o, flags=re.MULTILINE) + self.assertIsNotNone(m) + + def test_simple_ssml_via_synthesizer_node(self): + rospy.wait_for_service('synthesizer') + speech_synthesizer = rospy.ServiceProxy('synthesizer', Synthesizer) + + text = 'Mary has a little lamb, little lamb, little lamb.' + res = speech_synthesizer(text=text, metadata='''{"text_type":"ssml"}''') + self.assertIsNotNone(res) + self.assertTrue(type(res) is SynthesizerResponse) + + r = json.loads(res.result) + self.assertIn('Audio Type', r, 'result should contain audio type') + self.assertIn('Audio File', r, 'result should contain file path') + self.assertIn('Amazon Polly Response Metadata', r, 'result should contain metadata') + + audio_type = r['Audio Type'] + audio_file = r['Audio File'] + md = r['Amazon Polly Response Metadata'] + self.assertTrue("'HTTPStatusCode': 200," in md) + self.assertEqual('audio/ogg', audio_type) + self.assertTrue(audio_file.endswith('.ogg')) + + import subprocess + o = subprocess.check_output(['file', audio_file], stderr=subprocess.STDOUT) + import re + m = re.search(r'.*Ogg data, Vorbis audi.*', o, flags=re.MULTILINE) + self.assertIsNotNone(m) + + +if __name__ == '__main__': + rostest.rosrun(PKG, NAME, TestPlainText, sys.argv) diff --git a/tts/test/test_unit_polly.py b/tts/test/test_unit_polly.py new file mode 100755 index 0000000..98d18a2 --- /dev/null +++ b/tts/test/test_unit_polly.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +from __future__ import print_function + + +from mock import patch, MagicMock # python2 uses backport of unittest.mock(docs.python.org/3/library/unittest.mock.html) +import unittest + + +class TestPolly(unittest.TestCase): + + def setUp(self): + """important: import tts which is a relay package:: + + devel/lib/python2.7/dist-packages/ + +-- tts + | +-- __init__.py + +-- ... + + per http://docs.ros.org/api/catkin/html/user_guide/setup_dot_py.html: + + A relay package is a folder with an __init__.py folder and nothing else. + Importing this folder in python will execute the contents of __init__.py, + which will in turn import the original python modules in the folder in + the sourcespace using the python exec() function. + """ + import tts + self.assertIsNotNone(tts) + + @patch('tts.amazonpolly.Session') + def test_init(self, boto3_session_class_mock): + from tts.amazonpolly import AmazonPolly + AmazonPolly() + + boto3_session_class_mock.assert_called() + boto3_session_class_mock.return_value.client.assert_called_with('polly') + + @patch('tts.amazonpolly.Session') + def test_defaults(self, boto3_session_class_mock): + from tts.amazonpolly import AmazonPolly + polly = AmazonPolly() + + boto3_session_class_mock.assert_called() + boto3_session_class_mock.return_value.client.assert_called_with('polly') + + self.assertEqual('text', polly.default_text_type) + self.assertEqual('ogg_vorbis', polly.default_output_format) + self.assertEqual('Joanna', polly.default_voice_id) + self.assertEqual('.', polly.default_output_folder) + self.assertEqual('output', polly.default_output_file_basename) + + @patch('tts.amazonpolly.Session') + def test_good_synthesis_with_default_args(self, boto3_session_class_mock): + boto3_session_obj_mock = MagicMock() + boto3_polly_obj_mock = MagicMock() + boto3_polly_response_mock = MagicMock() + audio_stream_mock = MagicMock() + fake_audio_stream_data = 'I am audio.' + fake_audio_content_type = 'super tts' + fake_boto3_polly_response_metadata = {'foo': 'bar'} + + boto3_session_class_mock.return_value = boto3_session_obj_mock + boto3_session_obj_mock.client.return_value = boto3_polly_obj_mock + boto3_polly_obj_mock.synthesize_speech.return_value = boto3_polly_response_mock + audio_stream_mock.read.return_value = fake_audio_stream_data + d = { + 'AudioStream': audio_stream_mock, + 'ContentType': fake_audio_content_type, + 'ResponseMetadata': fake_boto3_polly_response_metadata + } + boto3_polly_response_mock.__contains__.side_effect = d.__contains__ + boto3_polly_response_mock.__getitem__.side_effect = d.__getitem__ + + from tts.amazonpolly import AmazonPolly + polly_under_test = AmazonPolly() + + boto3_session_class_mock.assert_called() + boto3_session_obj_mock.client.assert_called_with('polly') + + res = polly_under_test.synthesize(text='hello') + + expected_synthesize_speech_kwargs = { + 'LexiconNames': [], + 'OutputFormat': 'ogg_vorbis', + 'SampleRate': '22050', + 'SpeechMarkTypes': [], + 'Text': 'hello', + 'TextType': 'text', + 'VoiceId': 'Joanna', + } + boto3_polly_obj_mock.synthesize_speech.assert_called_with(**expected_synthesize_speech_kwargs) + + from tts.srv import PollyResponse + self.assertTrue(isinstance(res, PollyResponse)) + + import json + j = json.loads(res.result) + observed_audio_file_content = open(j['Audio File']).read() + self.assertEqual(fake_audio_stream_data, observed_audio_file_content) + + self.assertEqual(fake_audio_content_type, j['Audio Type']) + self.assertEqual(str(fake_boto3_polly_response_metadata), j['Amazon Polly Response Metadata']) + + @patch('tts.amazonpolly.Session') + def test_polly_raises(self, boto3_session_class_mock): + boto3_session_obj_mock = MagicMock() + boto3_polly_obj_mock = MagicMock() + boto3_polly_response_mock = MagicMock() + audio_stream_mock = MagicMock() + fake_audio_stream_data = 'I am audio.' + fake_audio_content_type = 'super voice' + fake_boto3_polly_response_metadata = {'foo': 'bar'} + + boto3_session_class_mock.return_value = boto3_session_obj_mock + boto3_session_obj_mock.client.return_value = boto3_polly_obj_mock + boto3_polly_obj_mock.synthesize_speech.side_effect = RuntimeError('Amazon Polly Exception') + audio_stream_mock.read.return_value = fake_audio_stream_data + d = { + 'AudioStream': audio_stream_mock, + 'ContentType': fake_audio_content_type, + 'ResponseMetadata': fake_boto3_polly_response_metadata + } + boto3_polly_response_mock.__contains__.side_effect = d.__contains__ + boto3_polly_response_mock.__getitem__.side_effect = d.__getitem__ + + from tts.amazonpolly import AmazonPolly + polly_under_test = AmazonPolly() + + boto3_session_class_mock.assert_called() + boto3_session_obj_mock.client.assert_called_with('polly') + + res = polly_under_test.synthesize(text='hello') + + expected_synthesize_speech_kwargs = { + 'LexiconNames': [], + 'OutputFormat': 'ogg_vorbis', + 'SampleRate': '22050', + 'SpeechMarkTypes': [], + 'Text': 'hello', + 'TextType': 'text', + 'VoiceId': 'Joanna', + } + boto3_polly_obj_mock.synthesize_speech.assert_called_with(**expected_synthesize_speech_kwargs) + + from tts.srv import PollyResponse + self.assertTrue(isinstance(res, PollyResponse)) + + import json + j = json.loads(res.result) + self.assertTrue('Exception' in j) + self.assertTrue('Traceback' in j) + + @patch('tts.amazonpolly.AmazonPolly') + def test_cli(self, amazon_polly_class_mock): + import sys + with patch.object(sys, 'argv', ['polly_node.py', '-n', 'polly-node']): + from tts import amazonpolly + amazonpolly.main() + amazon_polly_class_mock.assert_called() + amazon_polly_class_mock.return_value.start.assert_called_with(node_name='polly-node', service_name='polly') + + +if __name__ == '__main__': + import rosunit + rosunit.unitrun('tts', 'unittest-polly', TestPolly) diff --git a/tts/test/test_unit_synthesizer.py b/tts/test/test_unit_synthesizer.py new file mode 100755 index 0000000..0148906 --- /dev/null +++ b/tts/test/test_unit_synthesizer.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +# Copyright (c) 2018, Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +from __future__ import print_function + +from mock import patch, MagicMock # python2 uses backport of unittest.mock(docs.python.org/3/library/unittest.mock.html) +import unittest + + +class TestSynthesizer(unittest.TestCase): + + def setUp(self): + """important: import tts which is a relay package:: + + devel/lib/python2.7/dist-packages/ + +-- tts + | +-- __init__.py + +-- ... + + per http://docs.ros.org/api/catkin/html/user_guide/setup_dot_py.html: + + A relay package is a folder with an __init__.py folder and nothing else. + Importing this folder in python will execute the contents of __init__.py, + which will in turn import the original python modules in the folder in + the sourcespace using the python exec() function. + """ + import tts + self.assertIsNotNone(tts) + + def test_init(self): + from tts.synthesizer import SpeechSynthesizer + speech_synthesizer = SpeechSynthesizer() + self.assertEqual('text', speech_synthesizer.default_text_type) + + @patch('tts.amazonpolly.AmazonPolly') + def test_good_synthesis_with_mostly_default_args_using_polly_lib(self, polly_class_mock): + polly_obj_mock = MagicMock() + polly_class_mock.return_value = polly_obj_mock + + test_text = 'hello' + test_metadata = ''' + { + "output_path": "/tmp/test" + } + ''' + expected_polly_synthesize_args = { + 'output_format': 'ogg_vorbis', + 'voice_id': 'Joanna', + 'sample_rate': '22050', + 'text_type': 'text', + 'text': test_text, + 'output_path': "/tmp/test" + } + + from tts.synthesizer import SpeechSynthesizer + from tts.srv import SynthesizerRequest + speech_synthesizer = SpeechSynthesizer(engine='POLLY_LIBRARY') + request = SynthesizerRequest(text=test_text, metadata=test_metadata) + response = speech_synthesizer._node_request_handler(request) + + polly_class_mock.assert_called() + polly_obj_mock.synthesize.assert_called_with(**expected_polly_synthesize_args) + + self.assertEqual(response.result, polly_obj_mock.synthesize.return_value.result) + + @patch('tts.amazonpolly.AmazonPolly') + def test_synthesis_with_bad_metadata_using_polly_lib(self, polly_class_mock): + polly_obj_mock = MagicMock() + polly_class_mock.return_value = polly_obj_mock + + test_text = 'hello' + test_metadata = '''I am no JSON''' + + from tts.synthesizer import SpeechSynthesizer + from tts.srv import SynthesizerRequest + speech_synthesizer = SpeechSynthesizer(engine='POLLY_LIBRARY') + request = SynthesizerRequest(text=test_text, metadata=test_metadata) + response = speech_synthesizer._node_request_handler(request) + + self.assertTrue(response.result.startswith('Exception: ')) + + @patch('tts.amazonpolly.AmazonPolly') + def test_bad_engine(self, polly_class_mock): + polly_obj_mock = MagicMock() + polly_class_mock.return_value = polly_obj_mock + + ex = None + + from tts.synthesizer import SpeechSynthesizer + try: + SpeechSynthesizer(engine='NON-EXIST ENGINE') + except Exception as e: + ex = e + + self.assertTrue(isinstance(ex, SpeechSynthesizer.BadEngineError)) + + def test_cli_help_message(self): + import os + source_file_dir = os.path.dirname(os.path.abspath(__file__)) + synthersizer_path = os.path.join(source_file_dir, '..', 'scripts', 'synthesizer_node.py') + import subprocess + o = subprocess.check_output(['python', synthersizer_path, '-h']) + self.assertTrue(str(o).startswith('Usage: ')) + + @patch('tts.synthesizer.SpeechSynthesizer') + def test_cli_engine_dispatching_1(self, speech_synthesizer_class_mock): + import sys + with patch.object(sys, 'argv', ['synthesizer_node.py']): + import tts.synthesizer + tts.synthesizer.main() + speech_synthesizer_class_mock.assert_called_with(engine='POLLY_SERVICE', polly_service_name='polly') + speech_synthesizer_class_mock.return_value.start.assert_called_with(node_name='synthesizer_node', + service_name='synthesizer') + + @patch('tts.synthesizer.SpeechSynthesizer') + def test_cli_engine_dispatching_2(self, speech_synthesizer_class_mock): + import sys + with patch.object(sys, 'argv', ['synthesizer_node.py', '-e', 'POLLY_LIBRARY']): + from tts import synthesizer + synthesizer.main() + speech_synthesizer_class_mock.assert_called_with(engine='POLLY_LIBRARY') + speech_synthesizer_class_mock.return_value.start.assert_called() + + @patch('tts.synthesizer.SpeechSynthesizer') + def test_cli_engine_dispatching_3(self, speech_synthesizer_class_mock): + import sys + with patch.object(sys, 'argv', ['synthesizer_node.py', '-p', 'apolly']): + from tts import synthesizer + synthesizer.main() + speech_synthesizer_class_mock.assert_called_with(engine='POLLY_SERVICE', polly_service_name='apolly') + speech_synthesizer_class_mock.return_value.start.assert_called() + + +if __name__ == '__main__': + import rosunit + rosunit.unitrun('tts', 'unittest-synthesizer', TestSynthesizer) diff --git a/wiki/images/cpu.svg b/wiki/images/cpu.svg new file mode 100644 index 0000000..54b4080 --- /dev/null +++ b/wiki/images/cpu.svg @@ -0,0 +1,4 @@ + +CPU %44668810101212141416161818202022220.0000.0000.0000.0000.0006.0896.0896.0896.0896.0896.08912.18012.18012.18012.18012.18012.18018.26818.26818.26818.26818.26818.26824.35124.35124.35124.35124.35124.35130.44730.44730.44730.44730.44730.44736.53936.53936.53936.53936.53936.53942.63142.63142.63142.63142.63142.63148.71648.71648.71648.71648.71648.71654.79954.79954.79954.79954.79954.79960.86260.86260.86260.86260.86260.86266.91766.91766.91766.91766.91766.91772.98572.98572.98572.98572.98572.98572.98579.05479.05479.05479.05479.05479.05485.10485.10485.10485.10485.10485.10491.15491.15491.15491.15491.15491.15497.23397.23397.23397.23397.23397.233103.317103.317103.317103.317103.317103.317109.365109.365109.365109.365109.365109.365115.472115.472115.472115.472115.472115.472121.523121.523121.523121.523121.523121.523127.567127.567127.567127.567127.567127.567133.620133.620133.620133.620133.620133.620139.701139.701139.701139.701139.701139.701145.750145.750145.750145.750145.750145.750151.804151.804151.804151.804151.804151.804157.857157.857157.857157.857157.857157.857163.949163.949163.949163.949163.949163.949163.949170.020170.020170.020170.020170.020170.020176.095176.095176.095176.095176.095176.095182.164182.164182.164182.164182.164182.164188.260188.260188.260188.260188.260188.260194.350194.350194.350194.350194.350194.350200.439200.439200.439200.439200.439200.439206.527206.527206.527206.527206.527206.527212.613212.613212.613212.613212.613212.613218.709218.709218.709218.709218.709218.709224.795224.795224.795224.795224.795224.795230.881230.881230.881230.881230.881230.881236.970236.970236.970236.970236.970236.970236.970243.056243.056243.056243.056243.056243.056249.150249.150249.150249.150249.150249.150255.244255.244255.244255.244255.244255.244261.330261.330261.330261.330261.330261.330267.416267.416267.416267.416267.416267.416273.502273.502273.502273.502273.502273.502279.589279.589279.589279.589279.589279.589285.675285.675285.675285.675285.675285.675291.761291.761291.761291.761291.761291.761297.847297.847297.847297.847297.847297.847303.942303.942303.942303.942303.942303.942303.942310.029310.029310.029310.029310.029310.029316.116316.116316.116316.116316.116316.116322.208322.208322.208322.208322.208322.208328.293328.293328.293328.293328.293328.293334.383334.383334.383334.383334.383334.383340.468340.468340.468340.468340.468340.468346.552346.552346.552346.552346.552346.552352.638352.638352.638352.638352.638352.638358.724358.724358.724358.724358.724358.724364.809364.809364.809364.809364.809364.809370.898370.898370.898370.898370.898370.898376.990376.990376.990376.990376.990376.990376.990383.080383.080383.080383.080383.080383.080389.165389.165389.165389.165389.165389.165395.249395.249395.249395.249395.249395.249401.333401.333CPU %time16.7514.330769230769231125.18982473222980.00016.7516.117648187224248125.18982473222980.00016.7517.904527143679264125.18982473222980.00016.7519.69140610013428125.18982473222980.00016.7521.478285056589296125.18982473222980.00015.523.265164013044316151.735589094449846.08915.525.052042969499333151.735589094449846.08915.526.838921925954345151.735589094449846.08915.528.62580088240936151.735589094449846.08915.530.412679838864378151.735589094449846.08915.532.1995587953194151.735589094449846.0891333.98643775177442204.8271178188899712.1801335.77331670822943204.8271178188899712.1801337.56019566468444204.8271178188899712.1801339.347074621139456204.8271178188899712.1801341.13395357759448204.8271178188899712.1801342.920832534049495204.8271178188899712.1801244.70771149050451226.06372930866618.2681246.49459044695952226.06372930866618.2681248.28146940341454226.06372930866618.2681250.06834835986957226.06372930866618.2681251.85522731632457226.06372930866618.2681253.642106272779586226.06372930866618.2681155.428985229234605247.3003407984420524.3511157.21586418568962247.3003407984420524.3511159.002743142144645247.3003407984420524.3511160.78962209859966247.3003407984420524.3511162.57650105505467247.3003407984420524.3511164.36338001150969247.3003407984420524.35110.2566.15025896796472263.227799415774130.44710.2567.93713792441973263.227799415774130.44710.2569.72401688087474263.227799415774130.44710.2571.51089583732976263.227799415774130.44710.2573.29777479378477263.227799415774130.44710.2575.0846537502398263.227799415774130.4478.576.87153270669481300.391869522882236.5398.578.65841166314982300.391869522882236.5398.580.44529061960485300.391869522882236.5398.582.23216957605985300.391869522882236.5398.584.01904853251487300.391869522882236.5398.585.80592748896989300.391869522882236.5397.7587.5928064454249316.319328140214242.6317.7589.37968540187993316.319328140214242.6317.7591.16656435833492316.319328140214242.6317.7592.95344331478994316.319328140214242.6317.7594.74032227124496316.319328140214242.6317.7596.52720122769999316.319328140214242.6317.2598.31408018415499326.9376338851022348.7167.25100.10095914061002326.9376338851022348.7167.25101.88783809706501326.9376338851022348.7167.25103.67471705352006326.9376338851022348.7167.25105.46159600997504326.9376338851022348.7167.25107.24847496643008326.9376338851022348.7166.75109.03535392288511337.5559396299902354.7996.75110.8222328793401337.5559396299902354.7996.75112.60911183579513337.5559396299902354.7996.75114.39599079225013337.5559396299902354.7996.75116.18286974870516337.5559396299902354.7996.75117.96974870516019337.5559396299902354.7996119.75662766161518353.4833982473222760.8626121.54350661807021353.4833982473222760.8626123.33038557452521353.4833982473222760.8626125.11726453098022353.4833982473222760.8626126.90414348743528353.4833982473222760.8626128.69102244389026353.4833982473222760.8628.75130.4779014003453295.082716650438266.9178.75132.2647803568003295.082716650438266.9178.75134.05165931325533295.082716650438266.9178.75135.83853826971034295.082716650438266.9178.75137.62541722616535295.082716650438266.9178.75139.41229618262037295.082716650438266.91710.25141.19917513907538263.227799415774172.98510.25142.98605409553042263.227799415774172.98510.25144.77293305198543263.227799415774172.98510.25146.55981200844042263.227799415774172.98510.25148.34669096489546263.227799415774172.98510.25150.13356992135047263.227799415774172.98510.25151.92044887780548263.227799415774172.98511.25153.7073278342605241.9911879259980579.05411.25155.4942067907155241.9911879259980579.05411.25157.28108574717052241.9911879259980579.05411.25159.06796470362556241.9911879259980579.05411.25160.85484366008058241.9911879259980579.05411.25162.64172261653556241.9911879259980579.05410.5164.42860157299057257.9186465433300685.10410.5166.2154805294456257.9186465433300685.10410.5168.00235948590063257.9186465433300685.10410.5169.78923844235564257.9186465433300685.10410.5171.57611739881065257.9186465433300685.10410.5173.36299635526566257.9186465433300685.10411.5175.1498753117207236.6820350535540291.15411.5176.93675426817572236.6820350535540291.15411.5178.72363322463073236.6820350535540291.15411.5180.51051218108574236.6820350535540291.15411.5182.29739113754079236.6820350535540291.15411.5184.0842700939958236.6820350535540291.15417.25185.87114905045078114.5715189873417397.23317.25187.65802800690582114.5715189873417397.23317.25189.44490696336084114.5715189873417397.23317.25191.23178591981585114.5715189873417397.23317.25193.01866487627086114.5715189873417397.23317.25194.80554383272593114.5715189873417397.23320196.592422789180956.17083739045762103.31720198.379301745635956.17083739045762103.31720200.1661807020909456.17083739045762103.31720201.9530596585459856.17083739045762103.31720203.73993861500156.17083739045762103.31720205.5268175714559856.17083739045762103.31722.25207.3136965279118.388461538461513109.36522.25209.100575484366038.388461538461513109.36522.25210.887454440821078.388461538461513109.36522.25212.67433339727618.388461538461513109.36522.25214.461212353731048.388461538461513109.36522.25216.24809131018618.388461538461513109.36520.5218.0349702666411245.552531645569616115.47220.5219.8218492230961445.552531645569616115.47220.5221.6087281795511545.552531645569616115.47220.5223.3956071360061645.552531645569616115.47220.5225.1824860924611845.552531645569616115.47220.5226.969365048916245.552531645569616115.47219.25228.7562440053712372.09829600778966121.52319.25230.5431229618262472.09829600778966121.52319.25232.3300019182812372.09829600778966121.52319.25234.1168808747362772.09829600778966121.52319.25235.9037598311912872.09829600778966121.52319.25237.690638787646372.09829600778966121.52319.75239.4775177441013361.47999026290165127.56719.75241.2643967005563261.47999026290165127.56719.75243.0512756570113361.47999026290165127.56719.75244.8381546134663761.47999026290165127.56719.75246.625033569921461.47999026290165127.56719.75248.4119125263764361.47999026290165127.56720.25250.1987914828313850.86168451801365133.62020.25251.9856704392864250.86168451801365133.62020.25253.7725493957414450.86168451801365133.62020.25255.5594283521964550.86168451801365133.62020.25257.346307308651550.86168451801365133.62020.25259.133186265106550.86168451801365133.62020.5260.9200652215614645.552531645569616139.70120.5262.7069441780165645.552531645569616139.70120.5264.4938231344715445.552531645569616139.70120.5266.280702090926645.552531645569616139.70120.5268.0675810473815745.552531645569616139.70120.5269.8544600038365545.552531645569616139.70119271.641338960291677.40744888023369145.75019273.4282179167466377.40744888023369145.75019275.215096873201677.40744888023369145.75019277.0019758296566677.40744888023369145.75019278.788854786111777.40744888023369145.75019280.575733742566777.40744888023369145.75021.75282.362612699021719.006767283349575151.80421.75284.149491655476719.006767283349575151.80421.75285.936370611931719.006767283349575151.80421.75287.7232495683867319.006767283349575151.80421.75289.510128524841819.006767283349575151.80421.75291.2970074812967619.006767283349575151.80420293.083886437751856.17083739045762157.85720294.8707653942068456.17083739045762157.85720296.657644350661856.17083739045762157.85720298.4445233071168756.17083739045762157.85720300.231402263571956.17083739045762157.85720302.0182812200268456.17083739045762157.85720.25303.805160176481950.86168451801365163.94920.25305.592039132936950.86168451801365163.94920.25307.378918089391950.86168451801365163.94920.25309.1657970458469450.86168451801365163.94920.25310.95267600230250.86168451801365163.94920.25312.7395549587569750.86168451801365163.94920.25314.52643391521250.86168451801365163.94918.75316.3133128716670582.71660175267766170.02018.75318.1001918281220482.71660175267766170.02018.75319.88707078457782.71660175267766170.02018.75321.67394974103282.71660175267766170.02018.75323.4608286974870582.71660175267766170.02018.75325.247707653942182.71660175267766170.02017.25327.03458661039707114.57151898734173176.09517.25328.8214655668521114.57151898734173176.09517.25330.60834452330715114.57151898734173176.09517.25332.39522347976214114.57151898734173176.09517.25334.1821024362172114.57151898734173176.09517.25335.9689813926722114.57151898734173176.09514.5337.75586034912715172.9722005842259182.16414.5339.5427393055822172.9722005842259182.16414.5341.32961826203723172.9722005842259182.16414.5343.1164972184922172.9722005842259182.16414.5344.9033761749473172.9722005842259182.16414.5346.6902551314023172.9722005842259182.16413.5348.4771340878573194.20881207400194188.26013.5350.2640130443123194.20881207400194188.26013.5352.05089200076736194.20881207400194188.26013.5353.83777095722235194.20881207400194188.26013.5355.62464991367733194.20881207400194188.26013.5357.4115288701324194.20881207400194188.26014.25359.19840782658736178.28135345666993194.35014.25360.9852867830424178.28135345666993194.35014.25362.7721657394975178.28135345666993194.35014.25364.5590446959524178.28135345666993194.35014.25366.3459236524074178.28135345666993194.35014.25368.1328026088625178.28135345666993194.35013.25369.91968156531743199.51796494644594200.43913.25371.70656052177253199.51796494644594200.43913.25373.4934394782275199.51796494644594200.43913.25375.28031843468256199.51796494644594200.43913.25377.0671973911376199.51796494644594200.43913.25378.8540763475926199.51796494644594200.43912380.6409553040476226.063729308666206.52712382.4278342605026226.063729308666206.52712384.21471321695765226.063729308666206.52712386.00159217341263226.063729308666206.52712387.7884711298676226.063729308666206.52712389.57535008632266226.063729308666206.52710.25391.3622290427777263.2277994157741212.61310.25393.14910799923274263.2277994157741212.61310.25394.9359869556877263.2277994157741212.61310.25396.7228659121427263.2277994157741212.61310.25398.5097448685978263.2277994157741212.61310.25400.29662382505273263.2277994157741212.6139.5402.08350278150783279.1552580331061218.7099.5403.8703817379628279.1552580331061218.7099.5405.6572606944178279.1552580331061218.7099.5407.44413965087284279.1552580331061218.7099.5409.2310186073279279.1552580331061218.7099.5411.01789756378287279.1552580331061218.70914.75412.8047765202379167.66304771178187224.79514.75414.5916554766929167.66304771178187224.79514.75416.37853443314793167.66304771178187224.79514.75418.1654133896029167.66304771178187224.79514.75419.952292346058167.66304771178187224.79514.75421.73917130251294167.66304771178187224.79513.5423.526050258968194.20881207400194230.88113.5425.312929215423194.20881207400194230.88113.5427.099808171878194.20881207400194230.88113.5428.8866871283331194.20881207400194230.88113.5430.6735660847881194.20881207400194230.88113.5432.460445041243194.20881207400194230.88111.5434.2473239976981236.68203505355402236.97011.5436.0342029541531236.68203505355402236.97011.5437.82108191060814236.68203505355402236.97011.5439.6079608670631236.68203505355402236.97011.5441.3948398235181236.68203505355402236.97011.5443.1817187799732236.68203505355402236.97011.5444.9685977364282236.68203505355402236.97010.5446.75547669288324257.91864654333006243.05610.5448.5423556493382257.91864654333006243.05610.5450.3292346057932257.91864654333006243.05610.5452.11611356224824257.91864654333006243.05610.5453.90299251870323257.91864654333006243.05610.5455.6898714751583257.91864654333006243.0569.75457.47675043161325273.8461051606621249.1509.75459.2636293880683273.8461051606621249.1509.75461.0505083445234273.8461051606621249.1509.75462.8373873009783273.8461051606621249.1509.75464.6242662574334273.8461051606621249.1509.75466.4111452138884273.8461051606621249.1508.75468.19802417034333295.0827166504382255.2448.75469.98490312679843295.0827166504382255.2448.75471.7717820832534295.0827166504382255.2448.75473.55866103970845295.0827166504382255.2448.75475.3455399961635295.0827166504382255.2448.75477.1324189526185295.0827166504382255.24410.25478.9192979090735263.2277994157741261.33010.25480.7061768655285263.2277994157741261.33010.25482.4930558219836263.2277994157741261.33010.25484.27993477843853263.2277994157741261.33010.25486.0668137348935263.2277994157741261.33010.25487.8536926913486263.2277994157741261.3308.5489.6405716478036300.3918695228822267.4168.5491.42745060425864300.3918695228822267.4168.5493.2143295607136300.3918695228822267.4168.5495.0012085171686300.3918695228822267.4168.5496.7880874736237300.3918695228822267.4168.5498.57496643007863300.3918695228822267.4167.75500.36184538653373316.3193281402142273.5027.75502.1487243429887316.3193281402142273.5027.75503.93560329944364316.3193281402142273.5027.75505.72248225589874316.3193281402142273.5027.75507.5093612123537316.3193281402142273.5027.75509.29624016880877316.3193281402142273.5027.25511.08311912526375326.93763388510223279.5897.25512.8699980817188326.93763388510223279.5897.25514.6568770381738326.93763388510223279.5897.25516.4437559946289326.93763388510223279.5897.25518.2306349510839326.93763388510223279.5897.25520.017513907539326.93763388510223279.5896.75521.8043928639938337.55593962999023285.6756.75523.5912718204489337.55593962999023285.6756.75525.3781507769039337.55593962999023285.6756.75527.165029733359337.55593962999023285.6756.75528.951908689814337.55593962999023285.6756.75530.738787646269337.55593962999023285.6755.5532.525666602724364.10170399221033291.7615.5534.312545559179364.10170399221033291.7615.5536.099424515634364.10170399221033291.7615.5537.886303472089364.10170399221033291.7615.5539.673182428544364.10170399221033291.7615.5541.4600613849991364.10170399221033291.7615.25543.246940341454369.4108568646543297.8475.25545.0338192979091369.4108568646543297.8475.25546.8206982543641369.4108568646543297.8475.25548.6075772108192369.4108568646543297.8475.25550.3944561672743369.4108568646543297.8475.25552.1813351237292369.4108568646543297.8474.75553.9682140801841380.02916260954237303.9424.75555.7550930366392380.02916260954237303.9424.75557.5419719930942380.02916260954237303.9424.75559.3288509495493380.02916260954237303.9424.75561.1157299060043380.02916260954237303.9424.75562.9026088624593380.02916260954237303.9424.75564.6894878189142380.02916260954237303.9424.25566.4763667753692390.6474683544304310.0294.25568.2632457318243390.6474683544304310.0294.25570.0501246882793390.6474683544304310.0294.25571.8370036447343390.6474683544304310.0294.25573.6238826011894390.6474683544304310.0294.25575.4107615576443390.6474683544304310.0294577.1976405140994395.9566212268744316.1164578.9845194705545395.9566212268744316.1164580.7713984270094395.9566212268744316.1164582.5582773834645395.9566212268744316.1164584.3451563399194395.9566212268744316.1164586.1320352963745395.9566212268744316.1163.25587.9189142528295411.88407984420644322.2083.25589.7057932092845411.88407984420644322.2083.25591.4926721657396411.88407984420644322.2083.25593.2795511221946411.88407984420644322.2083.25595.0664300786495411.88407984420644322.2083.25596.8533090351045411.88407984420644322.2083598.6401879915595417.1932327166504328.2933600.4270669480146417.1932327166504328.2933602.2139459044696417.1932327166504328.2933604.0008248609247417.1932327166504328.2933605.7877038173796417.1932327166504328.2933607.5745827738345417.1932327166504328.2932.75609.3614617302896422.50238558909444334.3832.75611.1483406867447422.50238558909444334.3832.75612.9352196431997422.50238558909444334.3832.75614.7220985996548422.50238558909444334.3832.75616.5089775561097422.50238558909444334.3832.75618.2958565125648422.50238558909444334.3832.5620.0827354690198427.8115384615385340.4682.5621.8696144254749427.8115384615385340.4682.5623.6564933819299427.8115384615385340.4682.5625.4433723383848427.8115384615385340.4682.5627.2302512948398427.8115384615385340.4682.5629.0171302512948427.8115384615385340.4686.5630.8040092077499342.86509250243427346.5526.5632.5908881642049342.86509250243427346.5526.5634.3777671206599342.86509250243427346.5526.5636.164646077115342.86509250243427346.5526.5637.9515250335699342.86509250243427346.5526.5639.738403990025342.86509250243427346.5525.5641.5252829464799364.10170399221033352.6385.5643.312161902935364.10170399221033352.6385.5645.09904085939364.10170399221033352.6385.5646.885919815845364.10170399221033352.6385.5648.6727987723001364.10170399221033352.6385.5650.4596777287551364.10170399221033352.6385652.2465566852101374.72000973709834358.7245654.0334356416652374.72000973709834358.7245655.8203145981201374.72000973709834358.7245657.6071935545751374.72000973709834358.7245659.3940725110301374.72000973709834358.7245661.1809514674851374.72000973709834358.7244.5662.9678304239402385.33831548198634364.8094.5664.7547093803952385.33831548198634364.8094.5666.5415883368503385.33831548198634364.8094.5668.3284672933052385.33831548198634364.8094.5670.1153462497601385.33831548198634364.8094.5671.9022252062152385.33831548198634364.8094.25673.6891041626702390.6474683544304370.8984.25675.4759831191253390.6474683544304370.8984.25677.2628620755804390.6474683544304370.8984.25679.0497410320353390.6474683544304370.8984.25680.8366199884904390.6474683544304370.8984.25682.6234989449454390.6474683544304370.8983.5684.4103779014005406.5749269717624376.9903.5686.1972568578555406.5749269717624376.9903.5687.9841358143104406.5749269717624376.9903.5689.7710147707654406.5749269717624376.9903.5691.5578937272204406.5749269717624376.9903.5693.3447726836755406.5749269717624376.9903.5695.1316516401305406.5749269717624376.9903.25696.9185305965855411.88407984420644383.0803.25698.7054095530405411.88407984420644383.0803.25700.4922885094954411.88407984420644383.0803.25702.2791674659505411.88407984420644383.0803.25704.0660464224055411.88407984420644383.0803.25705.8529253788606411.88407984420644383.0803707.6398043353156417.1932327166504389.1653709.4266832917706417.1932327166504389.1653711.2135622482256417.1932327166504389.1653713.0004412046807417.1932327166504389.1653714.7873201611357417.1932327166504389.1653716.5741991175906417.1932327166504389.1652.75718.3610780740456422.50238558909444395.2492.75720.1479570305007422.50238558909444395.2492.75721.9348359869557422.50238558909444395.2492.75723.7217149434107422.50238558909444395.2492.75725.5085938998657422.50238558909444395.2492.75727.2954728563207422.50238558909444395.2492.5729.0823518127759427.8115384615385401.3332.5730.8692307692307427.8115384615385401.33310.7514.330769230769231252.609493670886080.00010.7516.117648187224248252.609493670886080.00010.7517.904527143679264252.609493670886080.00010.7519.69140610013428252.609493670886080.00010.7521.478285056589296252.609493670886080.00010.523.265164013044316257.918646543330066.08910.525.052042969499333257.918646543330066.08910.526.838921925954345257.918646543330066.08910.528.62580088240936257.918646543330066.08910.530.412679838864378257.918646543330066.08910.532.1995587953194257.918646543330066.08910.2533.98643775177442263.227799415774112.18010.2535.77331670822943263.227799415774112.18010.2537.56019566468444263.227799415774112.18010.2539.347074621139456263.227799415774112.18010.2541.13395357759448263.227799415774112.18010.2542.920832534049495263.227799415774112.1801044.70771149050451268.536952288218118.2681046.49459044695952268.536952288218118.2681048.28146940341454268.536952288218118.2681050.06834835986957268.536952288218118.2681051.85522731632457268.536952288218118.2681053.642106272779586268.536952288218118.2689.7555.428985229234605273.846105160662124.3519.7557.21586418568962273.846105160662124.3519.7559.002743142144645273.846105160662124.3519.7560.78962209859966273.846105160662124.3519.7562.57650105505467273.846105160662124.3519.7564.36338001150969273.846105160662124.3519.7566.15025896796472273.846105160662130.4479.7567.93713792441973273.846105160662130.4479.7569.72401688087474273.846105160662130.4479.7571.51089583732976273.846105160662130.4479.7573.29777479378477273.846105160662130.4479.7575.0846537502398273.846105160662130.4479.2576.87153270669481284.4644109055501536.5399.2578.65841166314982284.4644109055501536.5399.2580.44529061960485284.4644109055501536.5399.2582.23216957605985284.4644109055501536.5399.2584.01904853251487284.4644109055501536.5399.2585.80592748896989284.4644109055501536.5399.2587.5928064454249284.4644109055501542.6319.2589.37968540187993284.4644109055501542.6319.2591.16656435833492284.4644109055501542.6319.2592.95344331478994284.4644109055501542.6319.2594.74032227124496284.4644109055501542.6319.2596.52720122769999284.4644109055501542.631998.31408018415499289.773563777994148.7169100.10095914061002289.773563777994148.7169101.88783809706501289.773563777994148.7169103.67471705352006289.773563777994148.7169105.46159600997504289.773563777994148.7169107.24847496643008289.773563777994148.7169109.03535392288511289.773563777994154.7999110.8222328793401289.773563777994154.7999112.60911183579513289.773563777994154.7999114.39599079225013289.773563777994154.7999116.18286974870516289.773563777994154.7999117.96974870516019289.773563777994154.7998.75119.75662766161518295.082716650438260.8628.75121.54350661807021295.082716650438260.8628.75123.33038557452521295.082716650438260.8628.75125.11726453098022295.082716650438260.8628.75126.90414348743528295.082716650438260.8628.75128.69102244389026295.082716650438260.8629.25130.4779014003453284.4644109055501566.9179.25132.2647803568003284.4644109055501566.9179.25134.05165931325533284.4644109055501566.9179.25135.83853826971034284.4644109055501566.9179.25137.62541722616535284.4644109055501566.9179.25139.41229618262037284.4644109055501566.9179.5141.19917513907538279.155258033106172.9859.5142.98605409553042279.155258033106172.9859.5144.77293305198543279.155258033106172.9859.5146.55981200844042279.155258033106172.9859.5148.34669096489546279.155258033106172.9859.5150.13356992135047279.155258033106172.9859.5151.92044887780548279.155258033106172.9859.75153.7073278342605273.846105160662179.0549.75155.4942067907155273.846105160662179.0549.75157.28108574717052273.846105160662179.0549.75159.06796470362556273.846105160662179.0549.75160.85484366008058273.846105160662179.0549.75162.64172261653556273.846105160662179.0549.75164.42860157299057273.846105160662185.1049.75166.2154805294456273.846105160662185.1049.75168.00235948590063273.846105160662185.1049.75169.78923844235564273.846105160662185.1049.75171.57611739881065273.846105160662185.1049.75173.36299635526566273.846105160662185.10410175.1498753117207268.536952288218191.15410176.93675426817572268.536952288218191.15410178.72363322463073268.536952288218191.15410180.51051218108574268.536952288218191.15410182.29739113754079268.536952288218191.15410184.0842700939958268.536952288218191.15411.25185.87114905045078241.9911879259980597.23311.25187.65802800690582241.9911879259980597.23311.25189.44490696336084241.9911879259980597.23311.25191.23178591981585241.9911879259980597.23311.25193.01866487627086241.9911879259980597.23311.25194.80554383272593241.9911879259980597.23311.75196.5924227891809231.37288218111004103.31711.75198.3793017456359231.37288218111004103.31711.75200.16618070209094231.37288218111004103.31711.75201.95305965854598231.37288218111004103.31711.75203.739938615001231.37288218111004103.31711.75205.52681757145598231.37288218111004103.31712.5207.313696527911215.44542356377798109.36512.5209.10057548436603215.44542356377798109.36512.5210.88745444082107215.44542356377798109.36512.5212.6743333972761215.44542356377798109.36512.5214.46121235373104215.44542356377798109.36512.5216.2480913101861215.44542356377798109.36512.25218.03497026664112220.75457643622198115.47212.25219.82184922309614220.75457643622198115.47212.25221.60872817955115220.75457643622198115.47212.25223.39560713600616220.75457643622198115.47212.25225.18248609246118220.75457643622198115.47212.25226.9693650489162220.75457643622198115.47212.25228.75624400537123220.75457643622198121.52312.25230.54312296182624220.75457643622198121.52312.25232.33000191828123220.75457643622198121.52312.25234.11688087473627220.75457643622198121.52312.25235.90375983119128220.75457643622198121.52312.25237.6906387876463220.75457643622198121.52312.5239.47751774410133215.44542356377798127.56712.5241.26439670055632215.44542356377798127.56712.5243.05127565701133215.44542356377798127.56712.5244.83815461346637215.44542356377798127.56712.5246.6250335699214215.44542356377798127.56712.5248.41191252637643215.44542356377798127.56712.75250.19879148283138210.136270691334133.62012.75251.98567043928642210.136270691334133.62012.75253.77254939574144210.136270691334133.62012.75255.55942835219645210.136270691334133.62012.75257.3463073086515210.136270691334133.62012.75259.1331862651065210.136270691334133.62013260.92006522156146204.82711781888997139.70113262.70694417801656204.82711781888997139.70113264.49382313447154204.82711781888997139.70113266.2807020909266204.82711781888997139.70113268.06758104738157204.82711781888997139.70113269.85446000383655204.82711781888997139.70112.75271.6413389602916210.136270691334145.75012.75273.42821791674663210.136270691334145.75012.75275.2150968732016210.136270691334145.75012.75277.00197582965666210.136270691334145.75012.75278.7888547861117210.136270691334145.75012.75280.5757337425667210.136270691334145.75013.5282.3626126990217194.20881207400194151.80413.5284.1494916554767194.20881207400194151.80413.5285.9363706119317194.20881207400194151.80413.5287.72324956838673194.20881207400194151.80413.5289.5101285248418194.20881207400194151.80413.5291.29700748129676194.20881207400194151.80413.25293.0838864377518199.51796494644594157.85713.25294.87076539420684199.51796494644594157.85713.25296.6576443506618199.51796494644594157.85713.25298.44452330711687199.51796494644594157.85713.25300.2314022635719199.51796494644594157.85713.25302.01828122002684199.51796494644594157.85713.5303.8051601764819194.20881207400194163.94913.5305.5920391329369194.20881207400194163.94913.5307.3789180893919194.20881207400194163.94913.5309.16579704584694194.20881207400194163.94913.5310.952676002302194.20881207400194163.94913.5312.73955495875697194.20881207400194163.94913.5314.526433915212194.20881207400194163.94913.25316.31331287166705199.51796494644594170.02013.25318.10019182812204199.51796494644594170.02013.25319.887070784577199.51796494644594170.02013.25321.673949741032199.51796494644594170.02013.25323.46082869748705199.51796494644594170.02013.25325.2477076539421199.51796494644594170.02013327.03458661039707204.82711781888997176.09513328.8214655668521204.82711781888997176.09513330.60834452330715204.82711781888997176.09513332.39522347976214204.82711781888997176.09513334.1821024362172204.82711781888997176.09513335.9689813926722204.82711781888997176.09512.5337.75586034912715215.44542356377798182.16412.5339.5427393055822215.44542356377798182.16412.5341.32961826203723215.44542356377798182.16412.5343.1164972184922215.44542356377798182.16412.5344.9033761749473215.44542356377798182.16412.5346.6902551314023215.44542356377798182.16412.25348.4771340878573220.75457643622198188.26012.25350.2640130443123220.75457643622198188.26012.25352.05089200076736220.75457643622198188.26012.25353.83777095722235220.75457643622198188.26012.25355.62464991367733220.75457643622198188.26012.25357.4115288701324220.75457643622198188.26012.5359.19840782658736215.44542356377798194.35012.5360.9852867830424215.44542356377798194.35012.5362.7721657394975215.44542356377798194.35012.5364.5590446959524215.44542356377798194.35012.5366.3459236524074215.44542356377798194.35012.5368.1328026088625215.44542356377798194.35012.25369.91968156531743220.75457643622198200.43912.25371.70656052177253220.75457643622198200.43912.25373.4934394782275220.75457643622198200.43912.25375.28031843468256220.75457643622198200.43912.25377.0671973911376220.75457643622198200.43912.25378.8540763475926220.75457643622198200.43912.25380.6409553040476220.75457643622198206.52712.25382.4278342605026220.75457643622198206.52712.25384.21471321695765220.75457643622198206.52712.25386.00159217341263220.75457643622198206.52712.25387.7884711298676220.75457643622198206.52712.25389.57535008632266220.75457643622198206.52711.75391.3622290427777231.37288218111004212.61311.75393.14910799923274231.37288218111004212.61311.75394.9359869556877231.37288218111004212.61311.75396.7228659121427231.37288218111004212.61311.75398.5097448685978231.37288218111004212.61311.75400.29662382505273231.37288218111004212.61311.5402.08350278150783236.68203505355402218.70911.5403.8703817379628236.68203505355402218.70911.5405.6572606944178236.68203505355402218.70911.5407.44413965087284236.68203505355402218.70911.5409.2310186073279236.68203505355402218.70911.5411.01789756378287236.68203505355402218.70912.5412.8047765202379215.44542356377798224.79512.5414.5916554766929215.44542356377798224.79512.5416.37853443314793215.44542356377798224.79512.5418.1654133896029215.44542356377798224.79512.5419.952292346058215.44542356377798224.79512.5421.73917130251294215.44542356377798224.79512.5423.526050258968215.44542356377798230.88112.5425.312929215423215.44542356377798230.88112.5427.099808171878215.44542356377798230.88112.5428.8866871283331215.44542356377798230.88112.5430.6735660847881215.44542356377798230.88112.5432.460445041243215.44542356377798230.88112434.2473239976981226.063729308666236.97012436.0342029541531226.063729308666236.97012437.82108191060814226.063729308666236.97012439.6079608670631226.063729308666236.97012441.3948398235181226.063729308666236.97012443.1817187799732226.063729308666236.97012444.9685977364282226.063729308666236.97011.75446.75547669288324231.37288218111004243.05611.75448.5423556493382231.37288218111004243.05611.75450.3292346057932231.37288218111004243.05611.75452.11611356224824231.37288218111004243.05611.75453.90299251870323231.37288218111004243.05611.75455.6898714751583231.37288218111004243.05611.5457.47675043161325236.68203505355402249.15011.5459.2636293880683236.68203505355402249.15011.5461.0505083445234236.68203505355402249.15011.5462.8373873009783236.68203505355402249.15011.5464.6242662574334236.68203505355402249.15011.5466.4111452138884236.68203505355402249.15011.25468.19802417034333241.99118792599805255.24411.25469.98490312679843241.99118792599805255.24411.25471.7717820832534241.99118792599805255.24411.25473.55866103970845241.99118792599805255.24411.25475.3455399961635241.99118792599805255.24411.25477.1324189526185241.99118792599805255.24411.5478.9192979090735236.68203505355402261.33011.5480.7061768655285236.68203505355402261.33011.5482.4930558219836236.68203505355402261.33011.5484.27993477843853236.68203505355402261.33011.5486.0668137348935236.68203505355402261.33011.5487.8536926913486236.68203505355402261.33011.25489.6405716478036241.99118792599805267.41611.25491.42745060425864241.99118792599805267.41611.25493.2143295607136241.99118792599805267.41611.25495.0012085171686241.99118792599805267.41611.25496.7880874736237241.99118792599805267.41611.25498.57496643007863241.99118792599805267.41611500.36184538653373247.30034079844205273.50211502.1487243429887247.30034079844205273.50211503.93560329944364247.30034079844205273.50211505.72248225589874247.30034079844205273.50211507.5093612123537247.30034079844205273.50211509.29624016880877247.30034079844205273.50210.75511.08311912526375252.60949367088608279.58910.75512.8699980817188252.60949367088608279.58910.75514.6568770381738252.60949367088608279.58910.75516.4437559946289252.60949367088608279.58910.75518.2306349510839252.60949367088608279.58910.75520.017513907539252.60949367088608279.58910.75521.8043928639938252.60949367088608285.67510.75523.5912718204489252.60949367088608285.67510.75525.3781507769039252.60949367088608285.67510.75527.165029733359252.60949367088608285.67510.75528.951908689814252.60949367088608285.67510.75530.738787646269252.60949367088608285.67510.25532.525666602724263.2277994157741291.76110.25534.312545559179263.2277994157741291.76110.25536.099424515634263.2277994157741291.76110.25537.886303472089263.2277994157741291.76110.25539.673182428544263.2277994157741291.76110.25541.4600613849991263.2277994157741291.76110543.246940341454268.5369522882181297.84710545.0338192979091268.5369522882181297.84710546.8206982543641268.5369522882181297.84710548.6075772108192268.5369522882181297.84710550.3944561672743268.5369522882181297.84710552.1813351237292268.5369522882181297.84710553.9682140801841268.5369522882181303.94210555.7550930366392268.5369522882181303.94210557.5419719930942268.5369522882181303.94210559.3288509495493268.5369522882181303.94210561.1157299060043268.5369522882181303.94210562.9026088624593268.5369522882181303.94210564.6894878189142268.5369522882181303.9429.75566.4763667753692273.8461051606621310.0299.75568.2632457318243273.8461051606621310.0299.75570.0501246882793273.8461051606621310.0299.75571.8370036447343273.8461051606621310.0299.75573.6238826011894273.8461051606621310.0299.75575.4107615576443273.8461051606621310.0299.5577.1976405140994279.1552580331061316.1169.5578.9845194705545279.1552580331061316.1169.5580.7713984270094279.1552580331061316.1169.5582.5582773834645279.1552580331061316.1169.5584.3451563399194279.1552580331061316.1169.5586.1320352963745279.1552580331061316.1169.25587.9189142528295284.46441090555015322.2089.25589.7057932092845284.46441090555015322.2089.25591.4926721657396284.46441090555015322.2089.25593.2795511221946284.46441090555015322.2089.25595.0664300786495284.46441090555015322.2089.25596.8533090351045284.46441090555015322.2089598.6401879915595289.7735637779941328.2939600.4270669480146289.7735637779941328.2939602.2139459044696289.7735637779941328.2939604.0008248609247289.7735637779941328.2939605.7877038173796289.7735637779941328.2939607.5745827738345289.7735637779941328.2939609.3614617302896289.7735637779941334.3839611.1483406867447289.7735637779941334.3839612.9352196431997289.7735637779941334.3839614.7220985996548289.7735637779941334.3839616.5089775561097289.7735637779941334.3839618.2958565125648289.7735637779941334.3838.75620.0827354690198295.0827166504382340.4688.75621.8696144254749295.0827166504382340.4688.75623.6564933819299295.0827166504382340.4688.75625.4433723383848295.0827166504382340.4688.75627.2302512948398295.0827166504382340.4688.75629.0171302512948295.0827166504382340.4689.5630.8040092077499279.1552580331061346.5529.5632.5908881642049279.1552580331061346.5529.5634.3777671206599279.1552580331061346.5529.5636.164646077115279.1552580331061346.5529.5637.9515250335699279.1552580331061346.5529.5639.738403990025279.1552580331061346.5529.25641.5252829464799284.46441090555015352.6389.25643.312161902935284.46441090555015352.6389.25645.09904085939284.46441090555015352.6389.25646.885919815845284.46441090555015352.6389.25648.6727987723001284.46441090555015352.6389.25650.4596777287551284.46441090555015352.6389652.2465566852101289.7735637779941358.7249654.0334356416652289.7735637779941358.7249655.8203145981201289.7735637779941358.7249657.6071935545751289.7735637779941358.7249659.3940725110301289.7735637779941358.7249661.1809514674851289.7735637779941358.7248.75662.9678304239402295.0827166504382364.8098.75664.7547093803952295.0827166504382364.8098.75666.5415883368503295.0827166504382364.8098.75668.3284672933052295.0827166504382364.8098.75670.1153462497601295.0827166504382364.8098.75671.9022252062152295.0827166504382364.8098.75673.6891041626702295.0827166504382370.8988.75675.4759831191253295.0827166504382370.8988.75677.2628620755804295.0827166504382370.8988.75679.0497410320353295.0827166504382370.8988.75680.8366199884904295.0827166504382370.8988.75682.6234989449454295.0827166504382370.8988.5684.4103779014005300.3918695228822376.9908.5686.1972568578555300.3918695228822376.9908.5687.9841358143104300.3918695228822376.9908.5689.7710147707654300.3918695228822376.9908.5691.5578937272204300.3918695228822376.9908.5693.3447726836755300.3918695228822376.9908.5695.1316516401305300.3918695228822376.9908.25696.9185305965855305.70102239532616383.0808.25698.7054095530405305.70102239532616383.0808.25700.4922885094954305.70102239532616383.0808.25702.2791674659505305.70102239532616383.0808.25704.0660464224055305.70102239532616383.0808.25705.8529253788606305.70102239532616383.0808707.6398043353156311.0101752677702389.1658709.4266832917706311.0101752677702389.1658711.2135622482256311.0101752677702389.1658713.0004412046807311.0101752677702389.1658714.7873201611357311.0101752677702389.1658716.5741991175906311.0101752677702389.1658718.3610780740456311.0101752677702395.2498720.1479570305007311.0101752677702395.2498721.9348359869557311.0101752677702395.2498723.7217149434107311.0101752677702395.2498725.5085938998657311.0101752677702395.2498727.2954728563207311.0101752677702395.2497.75729.0823518127759316.3193281402142401.3337.75730.8692307692307316.3193281402142401.3336.2514.330769230769231348.17424537487830.0006.2516.117648187224248348.17424537487830.0006.2517.904527143679264348.17424537487830.0006.2519.69140610013428348.17424537487830.0006.2521.478285056589296348.17424537487830.0006.2523.265164013044316348.17424537487836.0896.2525.052042969499333348.17424537487836.0896.2526.838921925954345348.17424537487836.0896.2528.62580088240936348.17424537487836.0896.2530.412679838864378348.17424537487836.0896.2532.1995587953194348.17424537487836.089633.98643775177442353.4833982473222712.180635.77331670822943353.4833982473222712.180637.56019566468444353.4833982473222712.180639.347074621139456353.4833982473222712.180641.13395357759448353.4833982473222712.180642.920832534049495353.4833982473222712.180644.70771149050451353.4833982473222718.268646.49459044695952353.4833982473222718.268648.28146940341454353.4833982473222718.268650.06834835986957353.4833982473222718.268651.85522731632457353.4833982473222718.268653.642106272779586353.4833982473222718.268655.428985229234605353.4833982473222724.351657.21586418568962353.4833982473222724.351659.002743142144645353.4833982473222724.351660.78962209859966353.4833982473222724.351662.57650105505467353.4833982473222724.351664.36338001150969353.4833982473222724.351666.15025896796472353.4833982473222730.447667.93713792441973353.4833982473222730.447669.72401688087474353.4833982473222730.447671.51089583732976353.4833982473222730.447673.29777479378477353.4833982473222730.447675.0846537502398353.4833982473222730.447676.87153270669481353.4833982473222736.539678.65841166314982353.4833982473222736.539680.44529061960485353.4833982473222736.539682.23216957605985353.4833982473222736.539684.01904853251487353.4833982473222736.539685.80592748896989353.4833982473222736.5395.7587.5928064454249358.792551119766342.6315.7589.37968540187993358.792551119766342.6315.7591.16656435833492358.792551119766342.6315.7592.95344331478994358.792551119766342.6315.7594.74032227124496358.792551119766342.6315.7596.52720122769999358.792551119766342.6315.7598.31408018415499358.792551119766348.7165.75100.10095914061002358.792551119766348.7165.75101.88783809706501358.792551119766348.7165.75103.67471705352006358.792551119766348.7165.75105.46159600997504358.792551119766348.7165.75107.24847496643008358.792551119766348.7165.75109.03535392288511358.792551119766354.7995.75110.8222328793401358.792551119766354.7995.75112.60911183579513358.792551119766354.7995.75114.39599079225013358.792551119766354.7995.75116.18286974870516358.792551119766354.7995.75117.96974870516019358.792551119766354.7995.75119.75662766161518358.792551119766360.8625.75121.54350661807021358.792551119766360.8625.75123.33038557452521358.792551119766360.8625.75125.11726453098022358.792551119766360.8625.75126.90414348743528358.792551119766360.8625.75128.69102244389026358.792551119766360.8626130.4779014003453353.4833982473222766.9176132.2647803568003353.4833982473222766.9176134.05165931325533353.4833982473222766.9176135.83853826971034353.4833982473222766.9176137.62541722616535353.4833982473222766.9176139.41229618262037353.4833982473222766.9176141.19917513907538353.4833982473222772.9856142.98605409553042353.4833982473222772.9856144.77293305198543353.4833982473222772.9856146.55981200844042353.4833982473222772.9856148.34669096489546353.4833982473222772.9856150.13356992135047353.4833982473222772.9856151.92044887780548353.4833982473222772.9856.25153.7073278342605348.174245374878379.0546.25155.4942067907155348.174245374878379.0546.25157.28108574717052348.174245374878379.0546.25159.06796470362556348.174245374878379.0546.25160.85484366008058348.174245374878379.0546.25162.64172261653556348.174245374878379.0546.25164.42860157299057348.174245374878385.1046.25166.2154805294456348.174245374878385.1046.25168.00235948590063348.174245374878385.1046.25169.78923844235564348.174245374878385.1046.25171.57611739881065348.174245374878385.1046.25173.36299635526566348.174245374878385.1046.25175.1498753117207348.174245374878391.1546.25176.93675426817572348.174245374878391.1546.25178.72363322463073348.174245374878391.1546.25180.51051218108574348.174245374878391.1546.25182.29739113754079348.174245374878391.1546.25184.0842700939958348.174245374878391.1546.75185.87114905045078337.5559396299902397.2336.75187.65802800690582337.5559396299902397.2336.75189.44490696336084337.5559396299902397.2336.75191.23178591981585337.5559396299902397.2336.75193.01866487627086337.5559396299902397.2336.75194.80554383272593337.5559396299902397.2337196.5924227891809332.24678675754626103.3177198.3793017456359332.24678675754626103.3177200.16618070209094332.24678675754626103.3177201.95305965854598332.24678675754626103.3177203.739938615001332.24678675754626103.3177205.52681757145598332.24678675754626103.3177.25207.313696527911326.93763388510223109.3657.25209.10057548436603326.93763388510223109.3657.25210.88745444082107326.93763388510223109.3657.25212.6743333972761326.93763388510223109.3657.25214.46121235373104326.93763388510223109.3657.25216.2480913101861326.93763388510223109.3657.25218.03497026664112326.93763388510223115.4727.25219.82184922309614326.93763388510223115.4727.25221.60872817955115326.93763388510223115.4727.25223.39560713600616326.93763388510223115.4727.25225.18248609246118326.93763388510223115.4727.25226.9693650489162326.93763388510223115.4727.25228.75624400537123326.93763388510223121.5237.25230.54312296182624326.93763388510223121.5237.25232.33000191828123326.93763388510223121.5237.25234.11688087473627326.93763388510223121.5237.25235.90375983119128326.93763388510223121.5237.25237.6906387876463326.93763388510223121.5237.25239.47751774410133326.93763388510223127.5677.25241.26439670055632326.93763388510223127.5677.25243.05127565701133326.93763388510223127.5677.25244.83815461346637326.93763388510223127.5677.25246.6250335699214326.93763388510223127.5677.25248.41191252637643326.93763388510223127.5677.5250.19879148283138321.6284810126582133.6207.5251.98567043928642321.6284810126582133.6207.5253.77254939574144321.6284810126582133.6207.5255.55942835219645321.6284810126582133.6207.5257.3463073086515321.6284810126582133.6207.5259.1331862651065321.6284810126582133.6207.5260.92006522156146321.6284810126582139.7017.5262.70694417801656321.6284810126582139.7017.5264.49382313447154321.6284810126582139.7017.5266.2807020909266321.6284810126582139.7017.5268.06758104738157321.6284810126582139.7017.5269.85446000383655321.6284810126582139.7017.5271.6413389602916321.6284810126582145.7507.5273.42821791674663321.6284810126582145.7507.5275.2150968732016321.6284810126582145.7507.5277.00197582965666321.6284810126582145.7507.5278.7888547861117321.6284810126582145.7507.5280.5757337425667321.6284810126582145.7507.75282.3626126990217316.3193281402142151.8047.75284.1494916554767316.3193281402142151.8047.75285.9363706119317316.3193281402142151.8047.75287.72324956838673316.3193281402142151.8047.75289.5101285248418316.3193281402142151.8047.75291.29700748129676316.3193281402142151.8047.75293.0838864377518316.3193281402142157.8577.75294.87076539420684316.3193281402142157.8577.75296.6576443506618316.3193281402142157.8577.75298.44452330711687316.3193281402142157.8577.75300.2314022635719316.3193281402142157.8577.75302.01828122002684316.3193281402142157.8577.75303.8051601764819316.3193281402142163.9497.75305.5920391329369316.3193281402142163.9497.75307.3789180893919316.3193281402142163.9497.75309.16579704584694316.3193281402142163.9497.75310.952676002302316.3193281402142163.9497.75312.73955495875697316.3193281402142163.9497.75314.526433915212316.3193281402142163.9497.75316.31331287166705316.3193281402142170.0207.75318.10019182812204316.3193281402142170.0207.75319.887070784577316.3193281402142170.0207.75321.673949741032316.3193281402142170.0207.75323.46082869748705316.3193281402142170.0207.75325.2477076539421316.3193281402142170.0207.75327.03458661039707316.3193281402142176.0957.75328.8214655668521316.3193281402142176.0957.75330.60834452330715316.3193281402142176.0957.75332.39522347976214316.3193281402142176.0957.75334.1821024362172316.3193281402142176.0957.75335.9689813926722316.3193281402142176.0957.75337.75586034912715316.3193281402142182.1647.75339.5427393055822316.3193281402142182.1647.75341.32961826203723316.3193281402142182.1647.75343.1164972184922316.3193281402142182.1647.75344.9033761749473316.3193281402142182.1647.75346.6902551314023316.3193281402142182.1647.5348.4771340878573321.6284810126582188.2607.5350.2640130443123321.6284810126582188.2607.5352.05089200076736321.6284810126582188.2607.5353.83777095722235321.6284810126582188.2607.5355.62464991367733321.6284810126582188.2607.5357.4115288701324321.6284810126582188.2607.75359.19840782658736316.3193281402142194.3507.75360.9852867830424316.3193281402142194.3507.75362.7721657394975316.3193281402142194.3507.75364.5590446959524316.3193281402142194.3507.75366.3459236524074316.3193281402142194.3507.75368.1328026088625316.3193281402142194.3507.75369.91968156531743316.3193281402142200.4397.75371.70656052177253316.3193281402142200.4397.75373.4934394782275316.3193281402142200.4397.75375.28031843468256316.3193281402142200.4397.75377.0671973911376316.3193281402142200.4397.75378.8540763475926316.3193281402142200.4397.5380.6409553040476321.6284810126582206.5277.5382.4278342605026321.6284810126582206.5277.5384.21471321695765321.6284810126582206.5277.5386.00159217341263321.6284810126582206.5277.5387.7884711298676321.6284810126582206.5277.5389.57535008632266321.6284810126582206.5277.5391.3622290427777321.6284810126582212.6137.5393.14910799923274321.6284810126582212.6137.5394.9359869556877321.6284810126582212.6137.5396.7228659121427321.6284810126582212.6137.5398.5097448685978321.6284810126582212.6137.5400.29662382505273321.6284810126582212.6137.5402.08350278150783321.6284810126582218.7097.5403.8703817379628321.6284810126582218.7097.5405.6572606944178321.6284810126582218.7097.5407.44413965087284321.6284810126582218.7097.5409.2310186073279321.6284810126582218.7097.5411.01789756378287321.6284810126582218.7097.75412.8047765202379316.3193281402142224.7957.75414.5916554766929316.3193281402142224.7957.75416.37853443314793316.3193281402142224.7957.75418.1654133896029316.3193281402142224.7957.75419.952292346058316.3193281402142224.7957.75421.73917130251294316.3193281402142224.7957.75423.526050258968316.3193281402142230.8817.75425.312929215423316.3193281402142230.8817.75427.099808171878316.3193281402142230.8817.75428.8866871283331316.3193281402142230.8817.75430.6735660847881316.3193281402142230.8817.75432.460445041243316.3193281402142230.8817.75434.2473239976981316.3193281402142236.9707.75436.0342029541531316.3193281402142236.9707.75437.82108191060814316.3193281402142236.9707.75439.6079608670631316.3193281402142236.9707.75441.3948398235181316.3193281402142236.9707.75443.1817187799732316.3193281402142236.9707.75444.9685977364282316.3193281402142236.9707.5446.75547669288324321.6284810126582243.0567.5448.5423556493382321.6284810126582243.0567.5450.3292346057932321.6284810126582243.0567.5452.11611356224824321.6284810126582243.0567.5453.90299251870323321.6284810126582243.0567.5455.6898714751583321.6284810126582243.0567.5457.47675043161325321.6284810126582249.1507.5459.2636293880683321.6284810126582249.1507.5461.0505083445234321.6284810126582249.1507.5462.8373873009783321.6284810126582249.1507.5464.6242662574334321.6284810126582249.1507.5466.4111452138884321.6284810126582249.1507.5468.19802417034333321.6284810126582255.2447.5469.98490312679843321.6284810126582255.2447.5471.7717820832534321.6284810126582255.2447.5473.55866103970845321.6284810126582255.2447.5475.3455399961635321.6284810126582255.2447.5477.1324189526185321.6284810126582255.2447.5478.9192979090735321.6284810126582261.3307.5480.7061768655285321.6284810126582261.3307.5482.4930558219836321.6284810126582261.3307.5484.27993477843853321.6284810126582261.3307.5486.0668137348935321.6284810126582261.3307.5487.8536926913486321.6284810126582261.3307.5489.6405716478036321.6284810126582267.4167.5491.42745060425864321.6284810126582267.4167.5493.2143295607136321.6284810126582267.4167.5495.0012085171686321.6284810126582267.4167.5496.7880874736237321.6284810126582267.4167.5498.57496643007863321.6284810126582267.4167.5500.36184538653373321.6284810126582273.5027.5502.1487243429887321.6284810126582273.5027.5503.93560329944364321.6284810126582273.5027.5505.72248225589874321.6284810126582273.5027.5507.5093612123537321.6284810126582273.5027.5509.29624016880877321.6284810126582273.5027.5511.08311912526375321.6284810126582279.5897.5512.8699980817188321.6284810126582279.5897.5514.6568770381738321.6284810126582279.5897.5516.4437559946289321.6284810126582279.5897.5518.2306349510839321.6284810126582279.5897.5520.017513907539321.6284810126582279.5897.25521.8043928639938326.93763388510223285.6757.25523.5912718204489326.93763388510223285.6757.25525.3781507769039326.93763388510223285.6757.25527.165029733359326.93763388510223285.6757.25528.951908689814326.93763388510223285.6757.25530.738787646269326.93763388510223285.6757.25532.525666602724326.93763388510223291.7617.25534.312545559179326.93763388510223291.7617.25536.099424515634326.93763388510223291.7617.25537.886303472089326.93763388510223291.7617.25539.673182428544326.93763388510223291.7617.25541.4600613849991326.93763388510223291.7617.25543.246940341454326.93763388510223297.8477.25545.0338192979091326.93763388510223297.8477.25546.8206982543641326.93763388510223297.8477.25548.6075772108192326.93763388510223297.8477.25550.3944561672743326.93763388510223297.8477.25552.1813351237292326.93763388510223297.8477.25553.9682140801841326.93763388510223303.9427.25555.7550930366392326.93763388510223303.9427.25557.5419719930942326.93763388510223303.9427.25559.3288509495493326.93763388510223303.9427.25561.1157299060043326.93763388510223303.9427.25562.9026088624593326.93763388510223303.9427.25564.6894878189142326.93763388510223303.9427.25566.4763667753692326.93763388510223310.0297.25568.2632457318243326.93763388510223310.0297.25570.0501246882793326.93763388510223310.0297.25571.8370036447343326.93763388510223310.0297.25573.6238826011894326.93763388510223310.0297.25575.4107615576443326.93763388510223310.0297577.1976405140994332.24678675754626316.1167578.9845194705545332.24678675754626316.1167580.7713984270094332.24678675754626316.1167582.5582773834645332.24678675754626316.1167584.3451563399194332.24678675754626316.1167586.1320352963745332.24678675754626316.1167587.9189142528295332.24678675754626322.2087589.7057932092845332.24678675754626322.2087591.4926721657396332.24678675754626322.2087593.2795511221946332.24678675754626322.2087595.0664300786495332.24678675754626322.2087596.8533090351045332.24678675754626322.2087598.6401879915595332.24678675754626328.2937600.4270669480146332.24678675754626328.2937602.2139459044696332.24678675754626328.2937604.0008248609247332.24678675754626328.2937605.7877038173796332.24678675754626328.2937607.5745827738345332.24678675754626328.2937609.3614617302896332.24678675754626334.3837611.1483406867447332.24678675754626334.3837612.9352196431997332.24678675754626334.3837614.7220985996548332.24678675754626334.3837616.5089775561097332.24678675754626334.3837618.2958565125648332.24678675754626334.3836.75620.0827354690198337.55593962999023340.4686.75621.8696144254749337.55593962999023340.4686.75623.6564933819299337.55593962999023340.4686.75625.4433723383848337.55593962999023340.4686.75627.2302512948398337.55593962999023340.4686.75629.0171302512948337.55593962999023340.4687630.8040092077499332.24678675754626346.5527632.5908881642049332.24678675754626346.5527634.3777671206599332.24678675754626346.5527636.164646077115332.24678675754626346.5527637.9515250335699332.24678675754626346.5527639.738403990025332.24678675754626346.5527641.5252829464799332.24678675754626352.6387643.312161902935332.24678675754626352.6387645.09904085939332.24678675754626352.6387646.885919815845332.24678675754626352.6387648.6727987723001332.24678675754626352.6387650.4596777287551332.24678675754626352.6387652.2465566852101332.24678675754626358.7247654.0334356416652332.24678675754626358.7247655.8203145981201332.24678675754626358.7247657.6071935545751332.24678675754626358.7247659.3940725110301332.24678675754626358.7247661.1809514674851332.24678675754626358.7246.75662.9678304239402337.55593962999023364.8096.75664.7547093803952337.55593962999023364.8096.75666.5415883368503337.55593962999023364.8096.75668.3284672933052337.55593962999023364.8096.75670.1153462497601337.55593962999023364.8096.75671.9022252062152337.55593962999023364.8096.75673.6891041626702337.55593962999023370.8986.75675.4759831191253337.55593962999023370.8986.75677.2628620755804337.55593962999023370.8986.75679.0497410320353337.55593962999023370.8986.75680.8366199884904337.55593962999023370.8986.75682.6234989449454337.55593962999023370.8986.75684.4103779014005337.55593962999023376.9906.75686.1972568578555337.55593962999023376.9906.75687.9841358143104337.55593962999023376.9906.75689.7710147707654337.55593962999023376.9906.75691.5578937272204337.55593962999023376.9906.75693.3447726836755337.55593962999023376.9906.75695.1316516401305337.55593962999023376.9906.75696.9185305965855337.55593962999023383.0806.75698.7054095530405337.55593962999023383.0806.75700.4922885094954337.55593962999023383.0806.75702.2791674659505337.55593962999023383.0806.75704.0660464224055337.55593962999023383.0806.75705.8529253788606337.55593962999023383.0806.75707.6398043353156337.55593962999023389.1656.75709.4266832917706337.55593962999023389.1656.75711.2135622482256337.55593962999023389.1656.75713.0004412046807337.55593962999023389.1656.75714.7873201611357337.55593962999023389.1656.75716.5741991175906337.55593962999023389.1656.75718.3610780740456337.55593962999023395.2496.75720.1479570305007337.55593962999023395.2496.75721.9348359869557337.55593962999023395.2496.75723.7217149434107337.55593962999023395.2496.75725.5085938998657337.55593962999023395.2496.75727.2954728563207337.55593962999023395.2496.5729.0823518127759342.86509250243427401.3336.5730.8692307692307342.86509250243427401.333Load Average (1min)Load Average (5min)Load Average (15min) \ No newline at end of file diff --git a/wiki/images/memory.svg b/wiki/images/memory.svg new file mode 100644 index 0000000..da9f3fd --- /dev/null +++ b/wiki/images/memory.svg @@ -0,0 +1,4 @@ + +Memory (MB)2102102202202302302402402502502602602702702802802902903003003103103203203303303403403503503603600.0000.0000.0000.0005.0375.0375.0375.0375.03710.06710.06710.06710.06710.06715.09615.09615.09615.09615.09620.12620.12620.12620.12620.12625.15625.15625.15625.15625.15630.18630.18630.18630.18630.18635.21935.21935.21935.21935.21940.24840.24840.24840.24840.24845.27845.27845.27845.27845.27850.30850.30850.30850.30850.30855.33855.33855.33855.33855.33860.37060.37060.37060.37060.37065.40365.40365.40365.40365.40370.42170.42170.42170.42170.42175.45475.45475.45475.45475.45480.47380.47380.47380.47380.47385.49085.49085.49085.49085.49090.51390.51390.51390.51390.51395.54595.54595.54595.54595.545100.569100.569100.569100.569100.569105.587105.587105.587105.587105.587110.602110.602110.602110.602110.602115.619115.619115.619115.619115.619120.635120.635120.635120.635120.635125.655125.655125.655125.655125.655130.671130.671130.671130.671130.671135.688135.688135.688135.688135.688140.722140.722140.722140.722140.722145.738145.738145.738145.738145.738150.754150.754150.754150.754150.754155.771155.771155.771155.771155.771160.789160.789160.789160.789160.789165.819165.819165.819165.819165.819170.837170.837170.837170.837170.837175.853175.853175.853175.853175.853180.883180.883180.883180.883180.883185.913185.913185.913185.913185.913190.943190.943190.943190.943190.943195.974195.974195.974195.974195.974200.991200.991200.991200.991200.991200.991206.023206.023206.023206.023206.023211.054211.054211.054211.054211.054216.084216.084216.084216.084216.084221.115221.115221.115221.115221.115226.146226.146226.146226.146226.146231.176231.176231.176231.176231.176236.192236.192236.192236.192236.192241.221241.221241.221241.221241.221246.252246.252246.252246.252246.252251.282251.282251.282251.282251.282256.313256.313256.313256.313256.313261.343261.343261.343261.343261.343266.373266.373266.373266.373266.373271.403271.403271.403271.403271.403276.433276.433276.433276.433276.433281.464281.464281.464281.464281.464286.494286.494286.494286.494286.494291.525291.525291.525291.525291.525296.556296.556296.556296.556296.556301.586301.586301.586301.586301.586306.617306.617306.617306.617306.617311.647311.647311.647311.647311.647316.677316.677316.677316.677316.677321.708321.708321.708321.708321.708326.738326.738326.738326.738326.738331.768331.768331.768331.768331.768336.799336.799336.799336.799336.799341.830341.830341.830341.830341.830346.860346.860346.860346.860346.860351.891351.891351.891351.891351.891356.907356.907356.907356.907356.907361.937361.937361.937361.937361.937366.967366.967366.967366.967366.967371.998371.998371.998371.998371.998371.998377.039377.039377.039377.039377.039382.068382.068382.068382.068382.068387.098387.098387.098387.098387.098392.129392.129392.129392.129392.129397.160397.160397.160397.160397.160402.190402.190Memory (MB)time24414.284615384615384324.985235732010.00024416.061308840413314324.985235732010.00024417.83800229621125324.985235732010.00024419.614695752009183324.985235732010.00022721.391389207807116370.9864764267995.03722723.168082663605052370.9864764267995.03722724.94477611940298370.9864764267995.03722726.721469575200917370.9864764267995.03722728.49816303099885370.9864764267995.03722730.274856486796782370.98647642679910.06722732.05154994259471370.98647642679910.06722733.82824339839265370.98647642679910.06722735.604936854190576370.98647642679910.06722737.38163030998851370.98647642679910.06722639.15832376578644373.692431761786615.09622640.93501722158438373.692431761786615.09622642.711710677382314373.692431761786615.09622644.48840413318025373.692431761786615.09622646.26509758897818373.692431761786615.09622648.04179104477612373.692431761786620.12622649.81848450057405373.692431761786620.12622651.595177956371984373.692431761786620.12622653.371871412169924373.692431761786620.12622655.148564867967856373.692431761786620.12622756.92525832376578370.98647642679925.15622758.701951779563714370.98647642679925.15622760.47864523536165370.98647642679925.15622762.25533869115959370.98647642679925.15622764.03203214695752370.98647642679925.15622765.80872560275544370.98647642679930.18622767.58541905855337370.98647642679930.18622769.36211251435131370.98647642679930.18622771.13880597014924370.98647642679930.18622772.91549942594717370.98647642679930.18622774.69219288174511370.98647642679935.21922776.46888633754305370.98647642679935.21922778.24557979334098370.98647642679935.21922780.02227324913892370.98647642679935.21922781.79896670493686370.98647642679935.21922783.57566016073478370.98647642679940.24822785.35235361653272370.98647642679940.24822787.12904707233065370.98647642679940.24822788.90574052812859370.98647642679940.24822790.68243398392651370.98647642679940.24822792.45912743972444370.98647642679945.27822794.2358208955224370.98647642679945.27822796.01251435132032370.98647642679945.27822797.78920780711825370.98647642679945.27822799.56590126291619370.98647642679945.278226101.34259471871411373.692431761786650.308226103.11928817451205373.692431761786650.308226104.89598163030998373.692431761786650.308226106.6726750861079373.692431761786650.308226108.44936854190586373.692431761786650.308227110.22606199770378370.98647642679955.338227112.00275545350169370.98647642679955.338227113.77944890929965370.98647642679955.338227115.55614236509757370.98647642679955.338227117.33283582089551370.98647642679955.338228119.10952927669344368.280521091811460.370228120.88622273249136368.280521091811460.370228122.6629161882893368.280521091811460.370228124.43960964408723368.280521091811460.370228126.21630309988517368.280521091811460.370250127.9929965556831308.749503722084465.403250129.76969001148103308.749503722084465.403250131.546383467279308.749503722084465.403250133.32307692307688308.749503722084465.403250135.09977037887484308.749503722084465.403228136.87646383467276368.280521091811470.421228138.65315729047072368.280521091811470.421228140.42985074626864368.280521091811470.421228142.20654420206657368.280521091811470.421228143.98323765786452368.280521091811470.421228145.75993111366242368.280521091811475.454228147.5366245694604368.280521091811475.454228149.3133180252583368.280521091811475.454228151.09001148105625368.280521091811475.454228152.86670493685418368.280521091811475.454229154.6433983926521365.5745657568238680.473229156.42009184845003365.5745657568238680.473229158.19678530424798365.5745657568238680.473229159.97347876004588365.5745657568238680.473229161.75017221584386365.5745657568238680.473228163.52686567164176368.280521091811485.490228165.3035591274397368.280521091811485.490228167.08025258323764368.280521091811485.490228168.85694603903556368.280521091811485.490228170.6336394948335368.280521091811485.490307172.41033295063144154.510049627791690.513307174.18702640642937154.510049627791690.513307175.96371986222732154.510049627791690.513307177.74041331802525154.510049627791690.513307179.5171067738232154.510049627791690.513336181.293800229621176.0373449131514695.545336183.0704936854190576.0373449131514695.545336184.8471871412169576.0373449131514695.545336186.6238805970149376.0373449131514695.545336188.4005740528128676.0373449131514695.545334190.1772675086107881.44925558312661100.569334191.953960964408781.44925558312661100.569334193.730654420206781.44925558312661100.569334195.507347876004681.44925558312661100.569334197.2840413318025481.44925558312661100.569335199.0607347876004478.74330024813901105.587335200.8374282433983678.74330024813901105.587335202.6141216991963278.74330024813901105.587335204.3908151549942778.74330024813901105.587335206.1675086107921778.74330024813901105.587333207.9442020665901284.15521091811422110.602333209.7208955223880284.15521091811422110.602333211.49758897818684.15521091811422110.602333213.2742824339839384.15521091811422110.602333215.0509758897818584.15521091811422110.602334216.8276693455797881.44925558312661115.619334218.604362801377781.44925558312661115.619334220.3810562571756681.44925558312661115.619334222.1577497129735881.44925558312661115.619334223.934443168771581.44925558312661115.619335225.7111366245694378.74330024813901120.635335227.4878300803673678.74330024813901120.635335229.2645235361653478.74330024813901120.635335231.0412169919632478.74330024813901120.635335232.817910447761278.74330024813901120.635335234.594603903559178.74330024813901125.655335236.3712973593570478.74330024813901125.655335238.14799081515578.74330024813901125.655335239.9246842709529278.74330024813901125.655335241.7013777267508578.74330024813901125.655335243.4780711825487778.74330024813901130.671335245.2547646383467378.74330024813901130.671335247.0314580941446878.74330024813901130.671335248.8081515499425878.74330024813901130.671335250.584845005740578.74330024813901130.671336252.361538461538476.03734491315146135.688336254.1382319173363876.03734491315146135.688336255.9149253731343476.03734491315146135.688336257.6916188289322376.03734491315146135.688336259.468312284730276.03734491315146135.688335261.245005740528178.74330024813901140.722335263.0216991963260478.74330024813901140.722335264.79839265212478.74330024813901140.722335266.5750861079219578.74330024813901140.722335268.3517795637198478.74330024813901140.722335270.1284730195177478.74330024813901145.738335271.905166475315778.74330024813901145.738335273.6818599311136578.74330024813901145.738335275.458553386911678.74330024813901145.738335277.235246842709578.74330024813901145.738333279.0119402985074584.15521091811422150.754333280.788633754305484.15521091811422150.754333282.565327210103384.15521091811422150.754333284.3420206659012684.15521091811422150.754333286.118714121699284.15521091811422150.754334287.895407577497181.44925558312661155.771334289.6721010332950681.44925558312661155.771334291.4487944890929681.44925558312661155.771334293.225487944890981.44925558312661155.771334295.0021814006888781.44925558312661155.771335296.7788748564867778.74330024813901160.789335298.555568312284778.74330024813901160.789335300.332261768082778.74330024813901160.789335302.1089552238805778.74330024813901160.789335303.885648679678578.74330024813901160.789359305.662342135476413.800372208436784165.819359307.4390355912744313.800372208436784165.819359309.2157290470723313.800372208436784165.819359310.992422502870213.800372208436784165.819359312.769115958668113.800372208436784165.819359314.5458094144661313.800372208436784170.837359316.322502870264113.800372208436784170.837359318.09919632606213.800372208436784170.837359319.875889781859913.800372208436784170.837359321.6525832376578413.800372208436784170.837358323.429276693455816.50632754342439175.853358325.2059701492537416.50632754342439175.853358326.9826636050516416.50632754342439175.853358328.759357060849616.50632754342439175.853358330.536050516647516.50632754342439175.853360332.3127439724454511.094416873449234180.883360334.0894374282433411.094416873449234180.883360335.8661308840413511.094416873449234180.883360337.6428243398392511.094416873449234180.883360339.4195177956371511.094416873449234180.883360341.196211251435111.094416873449234185.913360342.9729047072330611.094416873449234185.913360344.74959816303111.094416873449234185.913360346.526291618828911.094416873449234185.913360348.302985074626811.094416873449234185.913360350.079678530424811.094416873449234190.943360351.856371986222711.094416873449234190.943360353.6330654420206711.094416873449234190.943360355.4097588978185611.094416873449234190.943360357.186452353616511.094416873449234190.943361358.963145809414478.38846153846157195.974361360.73983926521238.38846153846157195.974361362.516532721010268.38846153846157195.974361364.29322617680828.38846153846157195.974361366.06991963260628.38846153846157195.974355367.846613088404124.62419354838721200.991355369.6233065442019724.62419354838721200.991355371.424.62419354838721200.991355373.1766934557979324.62419354838721200.991355374.953386911595824.62419354838721200.991355376.730080367393824.62419354838721200.991340378.506773823191765.21352357320103206.023340380.2834672789896365.21352357320103206.023340382.060160734787665.21352357320103206.023340383.836854190585565.21352357320103206.023340385.613547646383565.21352357320103206.023339387.3902411021813467.91947890818864211.054339389.166934557979367.91947890818864211.054339390.9436280137772467.91947890818864211.054339392.720321469575267.91947890818864211.054339394.4970149253731567.91947890818864211.054339396.27370838117167.91947890818864216.084339398.0504018369689467.91947890818864216.084339399.8270952927669667.91947890818864216.084339401.603788748564867.91947890818864216.084339403.3804822043627567.91947890818864216.084339405.157175660160767.91947890818864221.115339406.9338691159586667.91947890818864221.115339408.710562571756667.91947890818864221.115339410.4872560275544567.91947890818864221.115339412.2639494833524667.91947890818864221.115338414.0406429391503670.62543424317624226.146338415.817336394948370.62543424317624226.146338417.5940298507462770.62543424317624226.146338419.3707233065441670.62543424317624226.146338421.147416762342170.62543424317624226.146339422.9241102181467.91947890818864231.176339424.7008036739379767.91947890818864231.176339426.47749712973667.91947890818864231.176339428.254190585533867.91947890818864231.176339430.030884041331867.91947890818864231.176339431.8075774971296767.91947890818864236.192339433.584270952927767.91947890818864236.192339435.3609644087256467.91947890818864236.192339437.137657864523567.91947890818864236.192339438.9143513203214367.91947890818864236.192339440.691044776119467.91947890818864241.221339442.4677382319173467.91947890818864241.221339444.2444316877152367.91947890818864241.221339446.0211251435131367.91947890818864241.221339447.7978185993111467.91947890818864241.221338449.57451205510970.62543424317624246.252338451.3512055109069470.62543424317624246.252338453.127898966704970.62543424317624246.252338454.9045924225028470.62543424317624246.252338456.681285878300870.62543424317624246.252338458.4579793340986470.62543424317624251.282338460.234672789896670.62543424317624251.282338462.011366245694670.62543424317624251.282338463.788059701492570.62543424317624251.282338465.5647531572904570.62543424317624251.282339467.341446613088467.91947890818864256.313339469.118140068886367.91947890818864256.313339470.8948335246842667.91947890818864256.313339472.6715269804821667.91947890818864256.313339474.448220436280167.91947890818864256.313339476.224913892078167.91947890818864261.343339478.0016073478759667.91947890818864261.343339479.778300803673967.91947890818864261.343339481.554994259471867.91947890818864261.343339483.331687715269867.91947890818864261.343339485.108381171067867.91947890818864266.373339486.885074626865667.91947890818864266.373339488.6617680826635767.91947890818864266.373339490.4384615384614767.91947890818864266.373339492.215154994259467.91947890818864266.373338493.991848450057470.62543424317624271.403338495.768541905855270.62543424317624271.403338497.545235361653270.62543424317624271.403338499.321928817451270.62543424317624271.403338501.0986222732491370.62543424317624271.403339502.875315729047167.91947890818864276.433339504.65200918484567.91947890818864276.433339506.4287026406429467.91947890818864276.433339508.205396096440867.91947890818864276.433339509.9820895522387367.91947890818864276.433339511.7587830080367467.91947890818864281.464339513.535476463834667.91947890818864281.464339515.312169919632567.91947890818864281.464339517.088863375430567.91947890818864281.464339518.865556831228467.91947890818864281.464339520.642250287026467.91947890818864286.494339522.418943742824467.91947890818864286.494339524.195637198622367.91947890818864286.494339525.972330654420167.91947890818864286.494339527.749024110218167.91947890818864286.494339529.52571756601667.91947890818864291.525339531.302411021813967.91947890818864291.525339533.079104477611867.91947890818864291.525339534.855797933409867.91947890818864291.525339536.632491389207867.91947890818864291.525339538.409184845005767.91947890818864296.556339540.185878300803767.91947890818864296.556339541.962571756601667.91947890818864296.556339543.739265212399567.91947890818864296.556339545.515958668197467.91947890818864296.556338547.292652123995470.62543424317624301.586338549.069345579793370.62543424317624301.586338550.846039035591370.62543424317624301.586338552.622732491389170.62543424317624301.586338554.399425947187170.62543424317624301.586338556.17611940298570.62543424317624306.617338557.95281285878370.62543424317624306.617338559.729506314580970.62543424317624306.617338561.506199770378870.62543424317624306.617338563.282893226176870.62543424317624306.617339565.059586681974867.91947890818864311.647339566.836280137772667.91947890818864311.647339568.612973593570667.91947890818864311.647339570.389667049368467.91947890818864311.647339572.166360505166467.91947890818864311.647339573.943053960964367.91947890818864316.677339575.719747416762367.91947890818864316.677339577.496440872560267.91947890818864316.677339579.273134328358267.91947890818864316.677339581.049827784156267.91947890818864316.677339582.826521239954167.91947890818864321.708339584.60321469575267.91947890818864321.708339586.379908151549967.91947890818864321.708339588.156601607347767.91947890818864321.708339589.933295063145767.91947890818864321.708339591.709988518943867.91947890818864326.738339593.486681974741667.91947890818864326.738339595.263375430539667.91947890818864326.738339597.040068886337567.91947890818864326.738339598.816762342135567.91947890818864326.738339600.593455797933467.91947890818864331.768339602.370149253731367.91947890818864331.768339604.146842709529267.91947890818864331.768339605.923536165327267.91947890818864331.768339607.700229621125167.91947890818864331.768338609.476923076923170.62543424317624336.799338611.253616532720970.62543424317624336.799338613.030309988518970.62543424317624336.799338614.807003444316870.62543424317624336.799338616.583696900114870.62543424317624336.799339618.360390355912767.91947890818864341.830339620.137083811710767.91947890818864341.830339621.913777267508667.91947890818864341.830339623.690470723306567.91947890818864341.830339625.467164179104467.91947890818864341.830227627.2438576349024370.986476426799346.860227629.0205510907002370.986476426799346.860227630.7972445464982370.986476426799346.860227632.5739380022961370.986476426799346.860227634.3506314580941370.986476426799346.860228636.127324913892368.2805210918114351.891228637.90401836969368.2805210918114351.891228639.680711825488368.2805210918114351.891228641.4574052812859368.2805210918114351.891228643.2340987370837368.2805210918114351.891228645.0107921928817368.2805210918114356.907228646.7874856486795368.2805210918114356.907228648.5641791044776368.2805210918114356.907228650.3408725602756368.2805210918114356.907228652.1175660160734368.2805210918114356.907227653.8942594718714370.986476426799361.937227655.6709529276693370.986476426799361.937227657.4476463834673370.986476426799361.937227659.2243398392652370.986476426799361.937227661.001033295063370.986476426799361.937228662.7777267508611368.2805210918114366.967228664.554420206659368.2805210918114366.967228666.3311136624569368.2805210918114366.967228668.1078071182549368.2805210918114366.967228669.8845005740527368.2805210918114366.967228671.6611940298507368.2805210918114371.998228673.4378874856485368.2805210918114371.998228675.2145809414466368.2805210918114371.998228676.9912743972445368.2805210918114371.998228678.7679678530425368.2805210918114371.998228680.5446613088404368.2805210918114371.998228682.3213547646383368.2805210918114377.039228684.0980482204362368.2805210918114377.039228685.8747416762342368.2805210918114377.039228687.651435132032368.2805210918114377.039228689.4281285878301368.2805210918114377.039228691.2048220436279368.2805210918114382.068228692.9815154994259368.2805210918114382.068228694.7582089552238368.2805210918114382.068228696.5349024110218368.2805210918114382.068228698.3115958668197368.2805210918114382.068227700.0882893226176370.986476426799387.098227701.8649827784155370.986476426799387.098227703.6416762342136370.986476426799387.098227705.4183696900114370.986476426799387.098227707.1950631458094370.986476426799387.098227708.9717566016072370.986476426799392.129227710.7484500574052370.986476426799392.129227712.5251435132031370.986476426799392.129227714.301836969001370.986476426799392.129227716.0785304247989370.986476426799392.129228717.855223880597368.2805210918114397.160228719.631917336395368.2805210918114397.160228721.4086107921929368.2805210918114397.160228723.1853042479906368.2805210918114397.160228724.9619977037886368.2805210918114397.160228726.7386911595866368.2805210918114402.190228728.5153846153845368.2805210918114402.19032414.284615384615384108.508808933002510.00032416.061308840413314108.508808933002510.00032417.83800229621125108.508808933002510.00032419.614695752009183108.508808933002510.00034221.39138920780711659.801612903225875.03734223.16808266360505259.801612903225875.03734224.9447761194029859.801612903225875.03734226.72146957520091759.801612903225875.03734228.4981630309988559.801612903225875.03734230.27485648679678259.8016129032258710.06734232.0515499425947159.8016129032258710.06734233.8282433983926559.8016129032258710.06734235.60493685419057659.8016129032258710.06734237.3816303099885159.8016129032258710.06734239.1583237657864459.8016129032258715.09634240.9350172215843859.8016129032258715.09634242.71171067738231459.8016129032258715.09634244.4884041331802559.8016129032258715.09634246.2650975889781859.8016129032258715.09634248.0417910447761259.8016129032258720.12634249.8184845005740559.8016129032258720.12634251.59517795637198459.8016129032258720.12634253.37187141216992459.8016129032258720.12634255.14856486796785659.8016129032258720.12634256.9252583237657859.8016129032258725.15634258.70195177956371459.8016129032258725.15634260.4786452353616559.8016129032258725.15634262.2553386911595959.8016129032258725.15634264.0320321469575259.8016129032258725.15634265.8087256027554459.8016129032258730.18634267.5854190585533759.8016129032258730.18634269.3621125143513159.8016129032258730.18634271.1388059701492459.8016129032258730.18634272.9154994259471759.8016129032258730.18634174.6921928817451162.5075682382134835.21934176.4688863375430562.5075682382134835.21934178.2455797933409862.5075682382134835.21934180.0222732491389262.5075682382134835.21934181.7989667049368662.5075682382134835.21934183.5756601607347862.5075682382134840.24834185.3523536165327262.5075682382134840.24834187.1290470723306562.5075682382134840.24834188.9057405281285962.5075682382134840.24834190.6824339839265162.5075682382134840.24834292.4591274397244459.8016129032258745.27834294.235820895522459.8016129032258745.27834296.0125143513203259.8016129032258745.27834297.7892078071182559.8016129032258745.27834299.5659012629161959.8016129032258745.278342101.3425947187141159.8016129032258750.308342103.1192881745120559.8016129032258750.308342104.8959816303099859.8016129032258750.308342106.672675086107959.8016129032258750.308342108.4493685419058659.8016129032258750.308342110.2260619977037859.8016129032258755.338342112.0027554535016959.8016129032258755.338342113.7794489092996559.8016129032258755.338342115.5561423650975759.8016129032258755.338342117.3328358208955159.8016129032258755.338340119.1095292766934465.2135235732010360.370340120.8862227324913665.2135235732010360.370340122.662916188289365.2135235732010360.370340124.4396096440872365.2135235732010360.370340126.2163030998851765.2135235732010360.370318127.9929965556831124.744540942928165.403318129.76969001148103124.744540942928165.403318131.546383467279124.744540942928165.403318133.32307692307688124.744540942928165.403318135.09977037887484124.744540942928165.403341136.8764638346727662.5075682382134870.421341138.6531572904707262.5075682382134870.421341140.4298507462686462.5075682382134870.421341142.2065442020665762.5075682382134870.421341143.9832376578645262.5075682382134870.421340145.7599311136624265.2135235732010375.454340147.536624569460465.2135235732010375.454340149.313318025258365.2135235732010375.454340151.0900114810562565.2135235732010375.454340152.8667049368541865.2135235732010375.454339154.643398392652167.9194789081886480.473339156.4200918484500367.9194789081886480.473339158.1967853042479867.9194789081886480.473339159.9734787600458867.9194789081886480.473339161.7501722158438667.9194789081886480.473340163.5268656716417665.2135235732010385.490340165.303559127439765.2135235732010385.490340167.0802525832376465.2135235732010385.490340168.8569460390355665.2135235732010385.490340170.633639494833565.2135235732010385.490261172.41033295063144278.983995037220990.513261174.18702640642937278.983995037220990.513261175.96371986222732278.983995037220990.513261177.74041331802525278.983995037220990.513261179.5171067738232278.983995037220990.513232181.2938002296211357.4566997518610495.545232183.07049368541905357.4566997518610495.545232184.84718714121695357.4566997518610495.545232186.62388059701493357.4566997518610495.545232188.40057405281286357.4566997518610495.545234190.17726750861078352.0447890818859100.569234191.9539609644087352.0447890818859100.569234193.7306544202067352.0447890818859100.569234195.5073478760046352.0447890818859100.569234197.28404133180254352.0447890818859100.569233199.06073478760044354.75074441687343105.587233200.83742824339836354.75074441687343105.587233202.61412169919632354.75074441687343105.587233204.39081515499427354.75074441687343105.587233206.16750861079217354.75074441687343105.587235207.94420206659012349.3388337468983110.602235209.72089552238802349.3388337468983110.602235211.497588978186349.3388337468983110.602235213.27428243398393349.3388337468983110.602235215.05097588978185349.3388337468983110.602234216.82766934557978352.0447890818859115.619234218.6043628013777352.0447890818859115.619234220.38105625717566352.0447890818859115.619234222.15774971297358352.0447890818859115.619234223.9344431687715352.0447890818859115.619233225.71113662456943354.75074441687343120.635233227.48783008036736354.75074441687343120.635233229.26452353616534354.75074441687343120.635233231.04121699196324354.75074441687343120.635233232.8179104477612354.75074441687343120.635233234.5946039035591354.75074441687343125.655233236.37129735935704354.75074441687343125.655233238.147990815155354.75074441687343125.655233239.92468427095292354.75074441687343125.655233241.70137772675085354.75074441687343125.655232243.47807118254877357.45669975186104130.671232245.25476463834673357.45669975186104130.671232247.03145809414468357.45669975186104130.671232248.80815154994258357.45669975186104130.671232250.5848450057405357.45669975186104130.671232252.3615384615384357.45669975186104135.688232254.13823191733638357.45669975186104135.688232255.91492537313434357.45669975186104135.688232257.69161882893223357.45669975186104135.688232259.4683122847302357.45669975186104135.688233261.2450057405281354.75074441687343140.722233263.02169919632604354.75074441687343140.722233264.798392652124354.75074441687343140.722233266.57508610792195354.75074441687343140.722233268.35177956371984354.75074441687343140.722232270.12847301951774357.45669975186104145.738232271.9051664753157357.45669975186104145.738232273.68185993111365357.45669975186104145.738232275.4585533869116357.45669975186104145.738232277.2352468427095357.45669975186104145.738234279.01194029850745352.0447890818859150.754234280.7886337543054352.0447890818859150.754234282.5653272101033352.0447890818859150.754234284.34202066590126352.0447890818859150.754234286.1187141216992352.0447890818859150.754234287.8954075774971352.0447890818859155.771234289.67210103329506352.0447890818859155.771234291.44879448909296352.0447890818859155.771234293.2254879448909352.0447890818859155.771234295.00218140068887352.0447890818859155.771232296.77887485648677357.45669975186104160.789232298.5555683122847357.45669975186104160.789232300.3322617680827357.45669975186104160.789232302.10895522388057357.45669975186104160.789232303.8856486796785357.45669975186104160.789208305.6623421354764422.39962779156326165.819208307.43903559127443422.39962779156326165.819208309.21572904707233422.39962779156326165.819208310.9924225028702422.39962779156326165.819208312.7691159586681422.39962779156326165.819208314.54580941446613422.39962779156326170.837208316.3225028702641422.39962779156326170.837208318.099196326062422.39962779156326170.837208319.8758897818599422.39962779156326170.837208321.65258323765784422.39962779156326170.837209323.4292766934558419.6936724565757175.853209325.20597014925374419.6936724565757175.853209326.98266360505164419.6936724565757175.853209328.7593570608496419.6936724565757175.853209330.5360505166475419.6936724565757175.853207332.31274397244545425.10558312655087180.883207334.08943742824334425.10558312655087180.883207335.86613088404135425.10558312655087180.883207337.64282433983925425.10558312655087180.883207339.41951779563715425.10558312655087180.883207341.1962112514351425.10558312655087185.913207342.97290470723306425.10558312655087185.913207344.749598163031425.10558312655087185.913207346.5262916188289425.10558312655087185.913207348.3029850746268425.10558312655087185.913206350.0796785304248427.8115384615385190.943206351.8563719862227427.8115384615385190.943206353.63306544202067427.8115384615385190.943206355.40975889781856427.8115384615385190.943206357.1864523536165427.8115384615385190.943206358.96314580941447427.8115384615385195.974206360.7398392652123427.8115384615385195.974206362.51653272101026427.8115384615385195.974206364.2932261768082427.8115384615385195.974206366.0699196326062427.8115384615385195.974211367.8466130884041414.2817617866005200.991211369.62330654420197414.2817617866005200.991211371.4414.2817617866005200.991211373.17669345579793414.2817617866005200.991211374.9533869115958414.2817617866005200.991211376.7300803673938414.2817617866005200.991226378.5067738231917373.6924317617866206.023226380.28346727898963373.6924317617866206.023226382.0601607347876373.6924317617866206.023226383.8368541905855373.6924317617866206.023226385.6135476463835373.6924317617866206.023228387.39024110218134368.2805210918114211.054228389.1669345579793368.2805210918114211.054228390.94362801377724368.2805210918114211.054228392.7203214695752368.2805210918114211.054228394.49701492537315368.2805210918114211.054228396.273708381171368.2805210918114216.084228398.05040183696894368.2805210918114216.084228399.82709529276696368.2805210918114216.084228401.6037887485648368.2805210918114216.084228403.38048220436275368.2805210918114216.084228405.1571756601607368.2805210918114221.115228406.93386911595866368.2805210918114221.115228408.7105625717566368.2805210918114221.115228410.48725602755445368.2805210918114221.115228412.26394948335246368.2805210918114221.115228414.04064293915036368.2805210918114226.146228415.8173363949483368.2805210918114226.146228417.59402985074627368.2805210918114226.146228419.37072330654416368.2805210918114226.146228421.1474167623421368.2805210918114226.146228422.92411021814368.2805210918114231.176228424.70080367393797368.2805210918114231.176228426.477497129736368.2805210918114231.176228428.2541905855338368.2805210918114231.176228430.0308840413318368.2805210918114231.176228431.80757749712967368.2805210918114236.192228433.5842709529277368.2805210918114236.192228435.36096440872564368.2805210918114236.192228437.1376578645235368.2805210918114236.192228438.91435132032143368.2805210918114236.192228440.6910447761194368.2805210918114241.221228442.46773823191734368.2805210918114241.221228444.24443168771523368.2805210918114241.221228446.02112514351313368.2805210918114241.221228447.79781859931114368.2805210918114241.221228449.574512055109368.2805210918114246.252228451.35120551090694368.2805210918114246.252228453.1278989667049368.2805210918114246.252228454.90459242250284368.2805210918114246.252228456.6812858783008368.2805210918114246.252228458.45797933409864368.2805210918114251.282228460.2346727898966368.2805210918114251.282228462.0113662456946368.2805210918114251.282228463.7880597014925368.2805210918114251.282228465.56475315729045368.2805210918114251.282227467.3414466130884370.986476426799256.313227469.1181400688863370.986476426799256.313227470.89483352468426370.986476426799256.313227472.67152698048216370.986476426799256.313227474.4482204362801370.986476426799256.313227476.2249138920781370.986476426799261.343227478.00160734787596370.986476426799261.343227479.7783008036739370.986476426799261.343227481.5549942594718370.986476426799261.343227483.3316877152698370.986476426799261.343228485.1083811710678368.2805210918114266.373228486.8850746268656368.2805210918114266.373228488.66176808266357368.2805210918114266.373228490.43846153846147368.2805210918114266.373228492.2151549942594368.2805210918114266.373228493.9918484500574368.2805210918114271.403228495.7685419058552368.2805210918114271.403228497.5452353616532368.2805210918114271.403228499.3219288174512368.2805210918114271.403228501.09862227324913368.2805210918114271.403227502.8753157290471370.986476426799276.433227504.652009184845370.986476426799276.433227506.42870264064294370.986476426799276.433227508.2053960964408370.986476426799276.433227509.98208955223873370.986476426799276.433227511.75878300803674370.986476426799281.464227513.5354764638346370.986476426799281.464227515.3121699196325370.986476426799281.464227517.0888633754305370.986476426799281.464227518.8655568312284370.986476426799281.464227520.6422502870264370.986476426799286.494227522.4189437428244370.986476426799286.494227524.1956371986223370.986476426799286.494227525.9723306544201370.986476426799286.494227527.7490241102181370.986476426799286.494228529.525717566016368.2805210918114291.525228531.3024110218139368.2805210918114291.525228533.0791044776118368.2805210918114291.525228534.8557979334098368.2805210918114291.525228536.6324913892078368.2805210918114291.525227538.4091848450057370.986476426799296.556227540.1858783008037370.986476426799296.556227541.9625717566016370.986476426799296.556227543.7392652123995370.986476426799296.556227545.5159586681974370.986476426799296.556228547.2926521239954368.2805210918114301.586228549.0693455797933368.2805210918114301.586228550.8460390355913368.2805210918114301.586228552.6227324913891368.2805210918114301.586228554.3994259471871368.2805210918114301.586228556.176119402985368.2805210918114306.617228557.952812858783368.2805210918114306.617228559.7295063145809368.2805210918114306.617228561.5061997703788368.2805210918114306.617228563.2828932261768368.2805210918114306.617227565.0595866819748370.986476426799311.647227566.8362801377726370.986476426799311.647227568.6129735935706370.986476426799311.647227570.3896670493684370.986476426799311.647227572.1663605051664370.986476426799311.647227573.9430539609643370.986476426799316.677227575.7197474167623370.986476426799316.677227577.4964408725602370.986476426799316.677227579.2731343283582370.986476426799316.677227581.0498277841562370.986476426799316.677227582.8265212399541370.986476426799321.708227584.603214695752370.986476426799321.708227586.3799081515499370.986476426799321.708227588.1566016073477370.986476426799321.708227589.9332950631457370.986476426799321.708227591.7099885189438370.986476426799326.738227593.4866819747416370.986476426799326.738227595.2633754305396370.986476426799326.738227597.0400688863375370.986476426799326.738227598.8167623421355370.986476426799326.738227600.5934557979334370.986476426799331.768227602.3701492537313370.986476426799331.768227604.1468427095292370.986476426799331.768227605.9235361653272370.986476426799331.768227607.7002296211251370.986476426799331.768227609.4769230769231370.986476426799336.799227611.2536165327209370.986476426799336.799227613.0303099885189370.986476426799336.799227614.8070034443168370.986476426799336.799227616.5836969001148370.986476426799336.799227618.3603903559127370.986476426799341.830227620.1370838117107370.986476426799341.830227621.9137772675086370.986476426799341.830227623.6904707233065370.986476426799341.830227625.4671641791044370.986476426799341.830339627.243857634902467.91947890818864346.860339629.020551090700267.91947890818864346.860339630.797244546498267.91947890818864346.860339632.573938002296167.91947890818864346.860339634.350631458094167.91947890818864346.860338636.12732491389270.62543424317624351.891338637.9040183696970.62543424317624351.891338639.68071182548870.62543424317624351.891338641.457405281285970.62543424317624351.891338643.234098737083770.62543424317624351.891338645.010792192881770.62543424317624356.907338646.787485648679570.62543424317624356.907338648.564179104477670.62543424317624356.907338650.340872560275670.62543424317624356.907338652.117566016073470.62543424317624356.907338653.894259471871470.62543424317624361.937338655.670952927669370.62543424317624361.937338657.447646383467370.62543424317624361.937338659.224339839265270.62543424317624361.937338661.00103329506370.62543424317624361.937338662.777726750861170.62543424317624366.967338664.55442020665970.62543424317624366.967338666.331113662456970.62543424317624366.967338668.107807118254970.62543424317624366.967338669.884500574052770.62543424317624366.967338671.661194029850770.62543424317624371.998338673.437887485648570.62543424317624371.998338675.214580941446670.62543424317624371.998338676.991274397244570.62543424317624371.998338678.767967853042570.62543424317624371.998338680.544661308840470.62543424317624371.998338682.321354764638370.62543424317624377.039338684.098048220436270.62543424317624377.039338685.874741676234270.62543424317624377.039338687.65143513203270.62543424317624377.039338689.428128587830170.62543424317624377.039338691.204822043627970.62543424317624382.068338692.981515499425970.62543424317624382.068338694.758208955223870.62543424317624382.068338696.534902411021870.62543424317624382.068338698.311595866819770.62543424317624382.068338700.088289322617670.62543424317624387.098338701.864982778415570.62543424317624387.098338703.641676234213670.62543424317624387.098338705.418369690011470.62543424317624387.098338707.195063145809470.62543424317624387.098338708.971756601607270.62543424317624392.129338710.748450057405270.62543424317624392.129338712.525143513203170.62543424317624392.129338714.30183696900170.62543424317624392.129338716.078530424798970.62543424317624392.129338717.85522388059770.62543424317624397.160338719.63191733639570.62543424317624397.160338721.408610792192970.62543424317624397.160338723.185304247990670.62543424317624397.160338724.961997703788670.62543424317624397.160338726.738691159586670.62543424317624402.190338728.515384615384570.62543424317624402.190Used MemoryFree Memory \ No newline at end of file