Skip to content

Commit

Permalink
Merge pull request #12 from ouspg/521275A-2023-speech-synthesis
Browse files Browse the repository at this point in the history
521275 a 2023 speech synthesis
  • Loading branch information
Aapo2001 authored Aug 8, 2023
2 parents 40b5ed2 + 1936c68 commit ba2c506
Show file tree
Hide file tree
Showing 18 changed files with 375 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
.vscode/*
!.vscode/c_cpp_properties.json

src/tts_package/resource/*
src/tts_package/resource/config.json
src/tts_package/resource/model.pth
src/tts_package/resource/output.wav

build
devel
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Joint -> Servo mappings are defined in two files. Configuration file connects se
* src/inmoov_description - robot files, which define the robot geometry and configuration for simulation (URDF, SRDF & rviz configuration)
* src/robot - robot launch files & servo controller configurations
* src/robot_hardware - hardware interface for ros2_controller, communicates with U2D2 via dynamixel workbench
* src/tts_package - Text-to-speech package for finnish speech synthesis

## Servo Table

Expand Down
32 changes: 32 additions & 0 deletions docs/BRINGUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ Anyway, you are able to test the face tracking and eye movements like this.

**Note: currently, only jaw, eyes, right hand & head pan movement can be simulated**

### Launching text-to-speech service

Text-to-speech works as a service which can be called from terminal utilizing the ros2 client in package.

Run the service in a (new) terminal

```console
ros2 run tts_package service
```

Call the service from terminal using client and synthetize speech

```console
ros2 run tts_package client "Tämä lause syntentisoidaan puheeksi."
```

## Bring-up real HW robot

### (0. Test servo communication)
Expand Down Expand Up @@ -142,6 +158,22 @@ Finally, start the eye movement node in a new terminal window
ros2 run eye_movement eye_movement_node
```

### 5. Launching text-to-speech service

Text-to-speech works as a service which can be called from terminal utilizing the ros2 client in package.

Run the service in a (new) terminal

```console
ros2 run tts_package service
```

Call the service from terminal using client and synthetize speech

```console
ros2 run tts_package client "Tämä lause syntentisoidaan puheeksi."
```

**Todo: simplify bring up process (add the starting of the controllers to the launch file)**

## Sending action goals manually
Expand Down
40 changes: 40 additions & 0 deletions src/tts_msgs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 3.5)
project(tts_msgs)

# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
"srv/StringToWav.srv"
)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()

ament_package()
23 changes: 23 additions & 0 deletions src/tts_msgs/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>tts_msgs</name>
<version>1.0.0</version>
<description>Interface for tts_package</description>
<maintainer email="[email protected]">Konsta Laurila</maintainer>
<license>TODO: License declaration</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<depend>geometry_msgs</depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
3 changes: 3 additions & 0 deletions src/tts_msgs/srv/StringToWav.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
string data
---
bool success
34 changes: 34 additions & 0 deletions src/tts_package/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
This package contains service and client for finnish text-to-speech feature. Service will automatically play synthesized speech when called with wanted sentence as an argument.

# Usage

## Before
Check that model.pth and config.json are located in src/tts_package/resource/ folder. These should be downloaded and installed automatically when installing environment with vagrant. Scripts located in /vagrant-scripts/bootstrap.sh.

## Dependencies

* `TTS`
* `espeak-ng`
* `simpleaudio`

These are included in the newest version of the vagrantfile. If these are not installed during bootstrap, they need to be installed to VM before starting the service.

Install TTS
> pip install TTS <br>
And install espeak
> apt -y install espeak
## Run TTS service
> ros2 run tts_package service

## Using the service
Service can be used by calling client with terminal, giving sentences as an argument. Note that sentences should be inside quotes and in finnish.
> ros2 run tts_package client "Hei. Tässä on lause joka syntentisoidaan puheeksi."
Service will now try to synthentize sentence into .wav file located in 'src/tts_package/resource/output.wav' which will then be played automatically.

## Potential future improvements

* Implement this feature to work with potential speech-to-text and chatbot features.
23 changes: 23 additions & 0 deletions src/tts_package/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>tts_package</name>
<version>1.0.0</version>
<description>Text-to-speech pacakge for synthetizing speech.</description>
<maintainer email="[email protected]">Konsta Laurila</maintainer>
<license>TODO: License declaration</license>

<depend>python3-TTS</depend>
<depend>python3-simpleaudio</depend>

<exec_depend>tts_msgs</exec_depend>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>
Empty file.
4 changes: 4 additions & 0 deletions src/tts_package/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[develop]
script-dir=$base/lib/tts_package
[install]
install-scripts=$base/lib/tts_package
27 changes: 27 additions & 0 deletions src/tts_package/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from setuptools import setup

package_name = 'tts_package'

setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='vagrant',
maintainer_email='[email protected]',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'service = tts_package.tts_node:main',
'client = tts_package.tts_member_function:main',
],
},
)
23 changes: 23 additions & 0 deletions src/tts_package/test/test_copyright.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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 ament_copyright.main import main
import pytest


@pytest.mark.copyright
@pytest.mark.linter
def test_copyright():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found errors'
25 changes: 25 additions & 0 deletions src/tts_package/test/test_flake8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2017 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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 ament_flake8.main import main_with_errors
import pytest


@pytest.mark.flake8
@pytest.mark.linter
def test_flake8():
rc, errors = main_with_errors(argv=[])
assert rc == 0, \
'Found %d code style errors / warnings:\n' % len(errors) + \
'\n'.join(errors)
23 changes: 23 additions & 0 deletions src/tts_package/test/test_pep257.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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 ament_pep257.main import main
import pytest


@pytest.mark.linter
@pytest.mark.pep257
def test_pep257():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found code style errors / warnings'
Empty file.
46 changes: 46 additions & 0 deletions src/tts_package/tts_package/tts_member_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import sys

from tts_msgs.srv import StringToWav

import rclpy
from rclpy.node import Node


class ttsClientAsync(Node):

# Client Node that is used to call the TTS service. Takes sentence as an argument that service will try to synthetize.
# ros2 run tts_package client "Tässä teksti joka syntentisoidaan. Voi sisältää useampiakin lauseita kunhan ne ovat lainausmerkkien sisällä".

def __init__(self):
super().__init__('tts_client_async')
self.cli = self.create_client(StringToWav, 'StringToWav')
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
self.req = StringToWav.Request()

def send_request(self, data):
self.req.data = data
self.future = self.cli.call_async(self.req)
rclpy.spin_until_future_complete(self, self.future)
return self.future.result()

def main():
rclpy.init()

tts_client = ttsClientAsync()

response = tts_client.send_request(sys.argv[1])

if(response.success):
tts_client.get_logger().info(
'Succesfully synthentized!')
else:
tts_client.get_logger().info(
'Failed to synthentize!'
)
tts_client.destroy_node()
rclpy.shutdown()


if __name__ == '__main__':
main()
Loading

0 comments on commit ba2c506

Please sign in to comment.